TypeScript内部函数参数类型推断异常的原因探究
在以下两个TypeScript代码示例中,第一个示例调用makeBox时,内部函数的参数input被推断为unknown;但仅为u函数添加一个never类型的参数_noop后,第二个示例里的内部函数参数input2就能正确推断为与t对应的string类型。这背后的逻辑是什么?
代码示例1
function makeBox<T, U extends () => (input: T) => number>(value: { t: T u: U }) { return value } makeBox({ t: 'ada', u: () => { return (input) => { return Number.parseInt(input) } }, })
代码示例2
function makeBox<T, U extends (noop: never) => (input: T) => number>(value: { t: T u: U }) { return value } makeBox({ t: 'ada', u: (_noop) => { return (input2) => { return Number.parseInt(input2) } }, })
背后的逻辑解析
核心原因在于TypeScript泛型推断的关联性与约束匹配机制:
示例1的问题:
泛型U的约束是() => (input: T) => number,也就是U是一个无参数函数,返回接收T类型参数的函数。但TypeScript在推断时,会优先独立处理U的类型——因为U不需要依赖任何外部参数,它会先把内部input的类型推断为unknown(毕竟没有明确的类型提示),之后才会推断T为string。但这时候U的类型已经确定,不会再反向更新input的类型,所以最终input还是unknown。示例2的解决原理:
当把U的约束改为(noop: never) => (input: T) => number后,U需要接收一个never类型的参数。never是TypeScript的底部类型,没有任何值能赋值给它,这就强制TypeScript必须严格按照约束来匹配U的类型:
因为_noop的类型只能是never,无法被推断为其他类型,TypeScript会将U的返回函数的input2类型与T强绑定。此时T从t: 'ada'推断为string,那么为了让U符合约束,返回函数的input2必须是T类型(也就是string),于是就实现了正确的类型推断。
简单来说,添加never参数相当于给TypeScript一个“强制关联锚点”,让它不能独立推断U的类型,必须把U的返回函数和T的类型绑定在一起推导,避免了类型推断的脱节问题。
内容的提问来源于stack exchange,提问作者Tony

