为何相似TypeScript代码报错不同?修改test函数后报错变化解析
TypeScript泛型函数类型推断差异问题解析
原始代码示例
type Convert<T> = { [P in keyof T]: T[P] extends string ? number : T[P] } function test<T>(target: T): Convert<T> { return target as any } interface Bar { a: number b: (_: any) => void } const bar: Bar = test({ a: '', // Type 'string' is not assignable to type 'number'. b: v => {} }) const bar2 = test({ a: '', b: v => {} // Parameter 'v' implicitly has an 'any' type. }) const bar3: Bar = test({ a: '', b: (v: any) => {} })
修改后的test函数
function test<T, R extends Convert<T>>(target: T): R { return target as any }
问题核心:泛型推断逻辑的反转
原始函数的类型检查流程
原始单泛型版本的test,TypeScript的处理顺序是:
- 先根据传入的对象字面量推断出
T的具体类型(比如bar中T被推断为{a: string, b: (v: any) => void}); - 通过
Convert<T>计算出返回值类型,得到{a: number, b: (v: any) => void}; - 最后同时做两项检查:返回值是否兼容
Bar,以及传入的target是否能合法转换为Convert<T>。这里因为target的a是string,但Convert<T>要求a是number,所以触发类型不匹配错误。
修改后函数的类型检查流程
双泛型版本的test,TypeScript的推断逻辑完全反转:
- 因为返回值要赋值给
Bar,所以直接把泛型R推断为Bar; - 接下来验证
Bar extends Convert<T>是否成立,这时候会反向推导T的类型:- 针对
Bar.a: number,根据Convert<T>的规则,只有T.a是string时,Convert<T>.a才会是number,所以T.a被推断为string; - 针对
Bar.b: (_: any) => void,Convert<T>.b等于T.b,所以T.b被推断为(_: any) => void;
- 针对
- 最后检查传入的
target是否匹配推断出的T——{a: '', b: v => {}}完全符合T的要求,因此不会再报a的类型错误,仅剩下未显式标注参数类型导致的implicit any警告。
内容的提问来源于stack exchange,提问作者Tycho
相关产品推荐
相关产品推荐

