为何已确认key为'push'时,Array[key]仍提示不可调用?
代码示例
function test<K extends keyof Array<number>>(target: Array<number>, key: K): void { if(key === 'push') { target[key](12); // 此处报错 } }
问题场景
已知target是Array<number>类型,key是Array的键(例如'push'),且已通过if(key === 'push')确认key等于'push'字符串字面量,但调用target[key](12)时仍出现如下报错:
This expression is not callable.
Not all constituents of type 'number | (() => IterableIterator) | (() => { copyWithin: boolean; entries: boolean; fill: boolean; find: boolean; findIndex: boolean; keys: boolean; values: boolean; }) | ... 28 more ... | ((searchElement: number, fromIndex?: number | undefined) => boolean)' are callable.
Type 'number' has no call signatures.
报错原因
keyof Array<number>包含的成员不只是数组方法名,还包括数字索引类型(即number)、length这类非函数属性。虽然你在代码里判断了key === 'push',但TypeScript无法通过这个条件判断来窄化泛型参数K的类型——因为K是keyof Array<number>的子类型,由函数调用方决定,TypeScript必须考虑所有符合约束的K的可能。因此target[key]的类型仍然是Array<number>[K],这个类型集合里包含了number(比如length属性)等不可调用的类型,所以TypeScript会抛出“无法调用”的错误。
是否为预期行为
这是TypeScript的预期行为。泛型参数的约束是全局的,函数内部的条件判断无法改变泛型参数的整体约束范围,TypeScript需要保证代码在所有符合泛型约束的输入下都能安全运行,因此不会因为局部的字面量判断就放宽类型检查。
内容的提问来源于stack exchange,提问作者Krzysztof Kaczyński

