Generics and case analysis do not currently play nicely together个
目前,TypeScrip不能使用control flow analysis来影响generic类型参数,比如在myFn
的正文中使用T
.这样做的主要障碍是,当类型参数是constrained到union type T extends "a" | "b"
时,not意味着T
要么是"a"
,要么是"b"
.T
也有可能是完整的联盟"a" | "b"
.目前还没有办法说T
必须是一个unions 的one个成员.因此,myFn()
可以这样调用:
myFn(Math.random() < 0.999 ? "a" : "b", "2"); // okay
// function myFn<"a" | "b">(valueType: "a" | "b", value: "1" | "2"): void
其中T
被推断为"a" | "b"
,而T extends "a" ? "1" : "2"
是distributive conditional type,这意味着valueType
和value
都是不相关的联合类型.因此,没有什么能阻止某人用valueType
的"a"
和value
的"2"
拨打myFn()
,事实上,上面的例子有99.9%的机会这样做.
在microsoft/TypeScript#27808有一个公开的功能请求,要求T
必须恰好是unions 的一个成员.也许在将来的Typescript 版本中,您可以写成这样的内容
// NOT VALID TYPESCRIPT, DON'T TRY THIS
function myFn<T oneof 'a' | 'b'>(valueType: T, value: T extends 'a' ? '1' : '2') {
if (valueType === 'a') {
value // value: "1"
}
}
myFn(Math.random() < 0.999 ? "a" : "b", "2"); // not allowed
但现在,它不是语言的一部分.除非这种情况发生变化,否则如果您判断像T
这样的泛型类型的值valueType
,它可能会对valueType
的类型产生影响,但对T
不会有任何影响.
Case analysis and discriminated unions do play nicely together个
我们不能将泛型用于控制流.
如果您想要使用控制流分析来判断一个变量的值,并使其影响另一个变量的表观类型,您将需要第一个变量是discriminated union type的判别式,并且两个变量都需要是destructured.
在不 destruct 的情况下,它看起来可能像是一个相当标准的歧视联盟:
type AcceptableArgs =
{ valueType: "a", value: "1" } |
{ valueType: "b", value: "2" };
function myFn(arg: AcceptableArgs) {
if (arg.valueType === 'a') {
arg.value // "1"
}
}
在这里你可以清楚地看到valueType
和value
之间的关系.或者valueType
是"a"
,value
是"1"
,或者valueType
是"b"
,value
是"2"
.
但想必您不想将对象传递给myFn
.我们可以将AcceptableArgs
设置为对应于myFn
的参数list的tuple type,而不是将其设置为普通对象类型.也就是说,我们将使AcceptableArgs
成为myFn
‘S rest parameter的类型:
type AcceptableArgs =
[valueType: "a", value: "1"] |
[valueType: "b", value: "2"];
function myFn(...[valueType, value]: AcceptableArgs) {
if (valueType === 'a') {
value // value: "1"
}
}
我们已经将REST参数分解为valueType
和value
变量,因此它的行为完全符合您的要求.或者,它可以写成:
type MyFn = (...args: AcceptableArgs) => void;
const myFn: MyFn = (valueType, value) => {
if (valueType === 'a') {
value // value: "1"
}
}
无论哪种方式,编译器都能够遵循valueType
和value
之间的关系,根据valueType
的值将value
缩小到"1"
或"2"
.
请注意,当您调用该函数时,您的IDE会将其表示为overloaded,如下所示:
myFn(
// ^-- 1/2 myFn(valueType: "a", value: "1"): void
// 2/2 myFn(valueType: "b", value: "2"): void
因此,从调用者的Angular 来看,它也应该是简单易用的.
Playground link to code个