想要理解 NoInfer 的设计必要性,首先需要理清 TypeScript 类型检查器的泛型推导规则与天然缺陷。
基础概念:协变、逆变、不变
泛型参数的子类型传递规则,直接决定类型推断的数据流方向。上方三态概念卡已展示协变(→ 输出)、逆变(← 输入)、不变(⇄ 双向)三种场景的核心差异——逆变是反向污染推导的根源,也是 NoInfer 主要针对的场景。
TS 原生双向泛型推断机制
当一个泛型函数存在两处可推导同一泛型 T 的位置时,类型检查器会合并两处来源的子类型,取二者的联合类型作为最终 T,这就是双向推导:
function pick<T>(arr: T[], key: T): T {
return arr.find(item => item === key)!
}
// 调用:pick([1, 2, 3], "abc")
// 两处推导源:arr 推导出 number,key 推导出 string
// TS 自动合并 T 为 string | number,产生不符合预期的联合类型
在工具函数、配置工厂、事件回调等场景中,双向推导会频繁出现类型发散、推断结果不符合业务预期的问题,这就是 TS 5.4 之前无法解决的核心痛点。
无 NoInfer 时传统兜底方案的缺陷
在 NoInfer 发布前,开发者解决双向推导冲突仅有三种方案,均存在明显短板:
- 拆分泛型参数,增加冗余类型变量新增无关联泛型占位,强行切断推导链路,代码冗余严重,提升维护成本。
- 使用
as const/ 强制类型断言as T破坏类型安全,失去 TS 静态校验能力,埋下隐性类型 Bug。 - 拆分函数为柯里化两层函数第一层固定泛型,第二层接收入参,多层嵌套大幅降低代码可读性。
以上方案均属于「规避问题」而非「根治底层推导逻辑」,而 NoInfer<T> 从类型检查器层面修改推导优先级,是官方标准解决方案。