TypeScript:传递回调而非函数引用导致类型变为unknown的问题
在以下示例中,变量a的类型为string(符合预期),但变量b的类型为unknown,且在x => doSomething(x)行出现TS错误:
Argument of type 'unknown' is not assignable to parameter of type 'string'.(2345)
(以下代码是真实业务代码的极简复现版本)
type F<a> = (x: a) => a function returnF<a>(f: F<a>): F<a> { return f } function doSomething(s: string): string { return s } const a = returnF( // 'a' is a string doSomething )("A") const b = returnF( // 'b' is unknown x => doSomething(x) )("B") const c = returnF<string>( // 'c' is a string x => doSomething(x) )("C")
技术问题
- 为何将
returnF(doSomething)改为returnF(x => doSomething(x))会引发上述类型错误? - 是否存在代码修改方案,使使用
returnF(x => doSomething(x))时无需每次显式指定泛型<a>?
问题1解答
直接传入doSomething时,TypeScript能从它的签名(s: string) => string直接匹配出F<string>,泛型参数a会被自动推断为string。
换成匿名函数x => doSomething(x)时,问题出在类型推导的循环依赖:匿名函数的参数x没有显式类型标注,TypeScript需要先确定x的类型才能推断returnF的泛型a,但反过来returnF的泛型a又决定了x的类型,形成了推导死循环。TypeScript无法自动解开这个循环,只能把x默认推断为unknown,导致doSomething的参数类型不匹配,同时returnF的泛型a也被推断为unknown,最终变量b的类型就是unknown。
问题2解答
有两种实用的修改方案:
方案一:给匿名函数参数显式标注类型
const b = returnF( (x: string) => doSomething(x) )("B")
给x标注string类型后,TypeScript能直接据此推断returnF的泛型a为string,无需手动指定泛型参数。
方案二:优化returnF的泛型推导逻辑
调整returnF的定义,让TypeScript优先从函数返回值的依赖反向推导参数类型:
type F<T> = (x: T) => T // 简化写法,让推导逻辑更清晰 function returnF<T>(f: (x: T) => T): F<T> { return f } const b = returnF(x => doSomething(x))("B") // 此时x自动推断为string,b的类型为string
这种写法下,TypeScript会先识别doSomething需要string类型参数,反向推导出匿名函数的x是string,进而确定泛型T为string,完美解决循环推导的问题。
内容的提问来源于stack exchange,提问作者Ytus

