问题是编译器不明白arg 和bar[fn] 是correlated。它将它们都视为不相关的联合类型,因此当大多数组合不是时,它期望 every combination of union constituents is possible。
在 TypeScript 3.2 中,您刚刚收到一条错误消息,指出 bar[fn] 没有调用签名,因为它是具有不同参数的函数的联合。我怀疑该代码的任何版本都可以在 TS2.6 中使用。当然,Parameters<> 的代码并不存在,因为直到 TS2.8 才引入条件类型。我尝试以与 TS2.6 兼容的方式重新创建您的代码,例如
interface B {
foo: MyNumberType,
bar: MyStringType,
baz:MyBooleanType
}
function test<T extends keyof Bar>(bar: Bar, fn: T) {
let arg: B[T]=null!
bar[fn](arg); // error here
}
和tested in TS2.7,但它仍然给出错误。所以我会假设这段代码从来没有真正起作用过。
关于never 问题:TypeScript 3.3 引入了support for calling unions of functions,要求参数是函数联合中参数的交集。在某些情况下这是一种改进,但在您的情况下,它希望参数是一堆不同字符串文字的交集,这些字符串文字被折叠到 never。这与以前的错误(“你不能这样称呼”)基本相同,但以更令人困惑的方式表示。
处理这个问题最直接的方法是使用type assertion,因为在这种情况下你比编译器更聪明:
function test<T extends keyof Bar>(bar: Bar, fn: T) {
let arg: Parameters<Bar[T]>[0] = null!; // give it some value
// assert that bar[fn] takes a union of args and returns a union of returns
(bar[fn] as (x: typeof arg) => ReturnType<Bar[T]>)(arg); // okay
}
类型断言是不安全的,这确实让你对编译器撒谎:
function evilTest<T extends keyof Bar>(bar: Bar, fn: T) {
// assertion below is lying to the compiler
(bar[fn] as (x: Parameters<Bar[T]>[0]) => ReturnType<Bar[T]>)("up"); // no error!
}
所以你应该小心。有一种方法可以对此进行完全类型安全的版本,强制编译器对所有可能性进行代码流分析:
function manualTest<T extends keyof Bar>(bar: Bar, fn: T): ReturnType<Bar[T]>;
// unions can be narrowed, generics cannot
// see https://github.com/Microsoft/TypeScript/issues/13995
// and https://github.com/microsoft/TypeScript/issues/24085
function manualTest(bar: Bar, fn: keyof Bar) {
switch (fn) {
case 'foo': {
let arg: Parameters<Bar[typeof fn]>[0] = null!
return bar[fn](arg);
}
case 'bar': {
let arg: Parameters<Bar[typeof fn]>[0] = null!
return bar[fn](arg);
}
case 'baz': {
let arg: Parameters<Bar[typeof fn]>[0] = null!
return bar[fn](arg);
}
default:
return assertUnreachable(fn);
}
}
但这太脆弱(如果您向Bar 添加方法,则需要更改代码)和重复(一遍又一遍的相同子句),我通常更喜欢上面的类型断言。
好的,希望对您有所帮助;祝你好运!