You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何相似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的处理顺序是:

  1. 先根据传入的对象字面量推断出T的具体类型(比如bar中T被推断为{a: string, b: (v: any) => void});
  2. 通过Convert<T>计算出返回值类型,得到{a: number, b: (v: any) => void};
  3. 最后同时做两项检查:返回值是否兼容Bar,以及传入的target是否能合法转换为Convert<T>。这里因为target的a是string,但Convert<T>要求a是number,所以触发类型不匹配错误。

修改后函数的类型检查流程

双泛型版本的test,TypeScript的推断逻辑完全反转:

  1. 因为返回值要赋值给Bar,所以直接把泛型R推断为Bar;
  2. 接下来验证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;
  3. 最后检查传入的target是否匹配推断出的T——{a: '', b: v => {}}完全符合T的要求,因此不会再报a的类型错误,仅剩下未显式标注参数类型导致的implicit any警告。

内容的提问来源于stack exchange,提问作者Tycho

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 16:35:17