是否可以让TypeScript正确推断funcAorB
函数中callback
参数的类型?当调用funcAorB
时,它可以从调用者的Angular 推断callback
参数的类型,但不能在funcAorB
的主体中推断。我希望既然TypeScript能够确定data
参数的正确类型,由于switch
语句测试key
成员,因此无论在函数中使用T
泛型类型,它都将使用相同的类型。
我喜欢在调用funcAorB
时正确地推断callback
的类型,我只是希望我能在funcAorB
中得到同样的推断。
打字机游戏场
type DataA = {
readonly key: 'a',
};
type DataB = {
readonly key: 'b',
};
function funcA(data: DataA, callback: (data: DataA) => void) {}
function funcB(data: DataB, callback: (data: DataB) => void) {}
function funcAorB<T extends DataA | DataB>(data: T, callback: (data: T) => void) {
switch (data.key) {
case 'a': {
// TypeScript believes that data is of type DataA now.
// But, I have to use "as" because TypeScript thinks that
// callback can be ((data: DataA) => void | (data: DataB) => void).
funcA(data, callback as (data: DataA) => void);
break;
}
case 'b': {
// TypeScript believes that data is of type DataB now.
// But, I have to use "as" because TypeScript thinks that
// callback can be ((data: DataA) => void | (data: DataB) => void).
funcB(data, callback as (data: DataB) => void);
break;
}
}
}
// The callback type is inferred properly at the call site.
funcAorB({ key: 'a' }, (data) => {
// This succeeds because data is inferred to be DataA.
const a: 'a' = data.key;
});
// The callback type is inferred properly at the call site.
funcAorB({ key: 'b' }, (data) => {
// This succeeds because data is inferred to be DataB.
const b: 'b' = data.key;
});
1条答案
按热度按时间ffx8fchx1#
TypeScript目前无法使用控制流分析来缩小或重新约束generic类型参数。它可以缩小泛型类型的 value 的表观类型,但泛型类型参数本身将顽固地保持不变。因此
data
被缩小为DataA
或DataB
,但T extends DataA | DataB
不变。其中一个原因是TypeScript的类型系统目前无法表达
T
需要发生什么。这里,
T
是使用完全联合类型Data | DataB
指定的。因此,将T
限制为仅DataA
或DataB
是不正确的,并且将T
重新约束为T extends DataA
或T extends DataB
也是不正确的。通过选中data.key
,你只能添加一个 lower-boundT
,就像特性请求microsoft/TypeScript#14520中描述的那样,格式可能是T super DataA extends DataA | DataB
或T super DataB extends DataA | DataB
,但是目前还没有办法在TypeScript中编写它。如果TypeScript * 可以 * 做到这一点,那么它可能会注意到传递给
funcA
的callback
必须是(data: DataA | DataB) => void
和(data: DataA) => void
之间的类型,这是安全的,并且您的函数将编译无误。有几个开放的特性请求要求支持基于控制流的泛型重新约束,其中一个是microsoft/TypeScript#33014,它允许使用泛型索引进行索引,就好像类型参数是下界一样。
另一个例子是microsoft/TypeScript#27808,它会显式禁止指定类型参数中的联合,毕竟,我想你实际上并不关心上面提到的
dAorB
的支持,而是想告诉编译器:“看,T
要么是"a"
,要么是"b"
,但绝对不会是并集"a" | "b"
“.如果我们 * 可以 * 使用类似T extends_oneof "a" | "b"
或T extends_oneof("a", "b")
的东西,那么大概编译器可以像对待值收缩一样对待类型参数收缩,事情会神奇地工作。无论如何,如果你关心这个被支持,你可能想去这些问题,给予他们一个👍,但务实地说,最好不要假设这将做任何事情,只是围绕它工作。
最简单的解决方法是像您一直在做的那样只使用类型Assert,更困难的是重构为使用泛型(如microsoft/TypeScript#47109中所述),以便您的
funcAorB
主体将执行编译器认为正确的单个操作(例如,funcs[data.key](data, callback)
对应于一个适当的通用funcs
)。既然您提到了这对您的用例不起作用,我就不在这里进一步讨论了。Playground代码链接