我不明白为什么在这个TypeScript函数中“value”没有缩小到number
。我在想,如果T
是Property.Height
,那么Data[T]
的类型应该是Data[Property.Height]
,也就是number
enum Property {
Height = 'height',
Name = 'name',
DateOfBirth = 'date_of_birth'
}
interface Data {
[Property.Height]: number;
[Property.Name]: string;
[Property.DateOfBirth]: Date;
}
function example<T extends Property>(value: Data[T], property: T) {
if (property === Property.Height) {
// why isn't "value" narrowed to number here?
}
}
1条答案
按热度按时间e4yzc0pl1#
TypeScript目前无法使用控制流分析来影响generic * 类型参数 *,例如
example
的主体内的T
。因此,虽然检查property === Property.Height
将缩小property
的类型,但T
类型参数本身将顽固地保持不变。T
没有改变的一个原因是,如果检查property === Property.Height
意味着T
就是Property.Height
,这是不正确的。约束T extends Property
并不意味着“T
恰好是Property
的联合成员之一”。它可以是Property
的任何子类型,包括完整的Property
联合本身。让我们看看如果我们用union类型调用example()`会发生什么:该调用是 allowed,您可以看到
T
被推断为unionProperty.Height | Property.DateOfBirth
,这意味着Data[T]
被推断为number | Date
。在这个调用中,有99.9%的机会property === Property.Height
,而value
* 绝对 *Date
。哎呀。在microsoft/TypeScript#27808上有一个长期的开放功能请求,要求其他语法而不是
T extends Property
。比如说T oneof Property
,意思是T
必须是Property
联合体的一个成员。然后,也许编译器可以使用property === Property.Height
得出结论,T
是Property.Height
,你会得到你想要的行为。但现在它不是语言的一部分。现在,如果你想在你的函数中使用
if
/else
或switch
/case
case分析,泛型不会真正帮助你。相反,支持的方式是与受歧视的工会。我可以将代码重构为:我不想离题太多来解释它是如何计算的,但是您可以看到现在
example
不是通用的;相反,它采用区分联合类型的rest parameter,其中第二个参数 * 区分 * 参数列表。你不能再用它打坏电话了,如上所示。Playground链接到代码