我正在尝试一个非常基本的(人为的)条件类型函数并遇到意外错误:
function test<
T
>(
maybeNumber: T
): T extends number ? number : string {
if (typeof maybeNumber === 'number') {
return maybeNumber // Type 'T & number' is not assignable to type 'T extends number ? number : string'.
}
return 'Not a number' // Type '"Not a number"' is not assignable to type 'T extends number ? number : string'.
}
Run Code Online (Sandbox Code Playgroud)
我认为这是条件类型的非常简单的用法,所以不确定发生了什么。有任何想法吗?
澄清一下,我并不是真的想实现这个特定的功能。我只是在试验条件类型,并想更好地理解为什么这实际上不起作用。
潜在的问题是 TypeScript 的编译器没有通过控制流分析来缩小泛型类型变量的类型。当您检查时(typeof maybeNumber === "number"),编译器可以将值 缩小maybeNumber为number,但不会将类型参数 缩小T为number。因此它无法验证number为返回类型赋值是否安全T extends number ? number : string。编译器将不得不执行一些它目前不做的分析,例如“好吧,如果typeof maybeNumber === "number",我们单独T从 的类型推断maybeNumber,那么在这个块中我们可以缩小T到number,因此我们应该返回一个类型的值number extends number ? number : string,也就是,number”。但这不会发生。
对于具有条件返回类型的泛型函数,这是一个相当大的痛点。关于此的规范的开放 GitHub 问题可能是microsoft/TypeScript#33912,但还有许多其他 GitHub 问题,这是主要问题。
所以这就是“为什么这行不通”的答案?
如果您对重构以使其工作不感兴趣,则可以忽略其余部分,但知道在这种情况下该做什么而不是等待语言更改可能仍然具有指导意义。
此处维护您的调用签名的最直接的解决方法是使您的函数成为单个签名重载,其中实现签名不是通用的。这实质上放松了实现内部的类型安全保证:
type MyConditional<T> = T extends number ? number : string;
type Unknown = string | number | boolean | {} | null | undefined;
function test<T>(maybeNumber: T): MyConditional<T>;
function test(maybeNumber: Unknown): MyConditional<Unknown> {
if (typeof maybeNumber === 'number') {
const ret: MyConditional<typeof maybeNumber> = maybeNumber;
return ret;
}
const ret: MyConditional<typeof maybeNumber> = "Not a number";
return ret;
}
Run Code Online (Sandbox Code Playgroud)
在这里,我尽可能地尝试保证类型安全,方法是使用ret注释为 as的临时变量,MyConditional<typeof maybeNumber>该变量使用maybeNumber. 如果您切换周围的检查(反过来,这至少会抱怨===到!==验证)。但通常我只是做一些像这样更简单的事情,让筹码掉到他们可能的地方:
function test2<T>(maybeNumber: T): MyConditional<T>;
function test2(maybeNumber: any): string | number {
if (typeof maybeNumber === 'number') {
return maybeNumber;
}
return "Not a number";
}
Run Code Online (Sandbox Code Playgroud)
好的,希望有帮助;祝你好运!
| 归档时间: |
|
| 查看次数: |
283 次 |
| 最近记录: |