为何可将不兼容函数传入不同TypeScript函数类型?
TypeScript strictFunctionTypes 配置下的函数类型兼容问题解析
开启strictFunctionTypes配置后,TypeScript的函数类型检查仍存在两个不符合直觉的场景,可传入不兼容函数导致运行时错误,具体原因如下:
type FooCallback = (param: boolean) => void; const fooCaller = (foo: FooCallback) => { foo(true); }; type BarCallback = () => void; const bartoFooPasser = (bar: BarCallback) => { // 1. 为什么没有TS错误?FooCallback需要boolean参数,但BarCallback没有定义参数! fooCaller(bar); }; type Baz = { param: { subparam: string; }; }; // 这个会报TS错误,符合预期 // const bar: BarCallback = (param: string) => {}; // 2. 为什么没有TS错误?BarCallback不需要参数,但仅仅因为obj有默认值就通过了? const barImplementer: BarCallback = (obj: Baz = { param: { subparam: '' }}) => { console.log(`The length of the string is ${obj.param.subparam.length}.`); }; // 这会导致运行时错误 :-/ bartoFooPasser(barImplementer);
问题1:无参数函数可赋值给带参数的void返回函数
这是TypeScript函数类型兼容规则的特殊设计:当函数返回值为void时,允许参数列表更短的函数赋值给参数列表更长的函数。
JavaScript本身允许函数接收多余参数并不做处理,TypeScript在void返回场景下遵循了这个特性——它认为调用FooCallback时传入的boolean参数会被BarCallback类型的函数忽略,因此允许这种赋值。但如果被赋值的函数内部实际依赖了未定义的参数(比如例子里的barImplementer),就会引发运行时错误,这属于类型系统在实用性和严格性之间的权衡。
问题2:带默认参数的函数可赋值给无参数函数
TypeScript会将带默认值的参数视为可选参数,因此(obj: Baz = ...) => void的类型等价于(obj?: Baz) => void。
在函数类型兼容规则中,期望无参数的函数类型(BarCallback)可以接受带可选参数的函数——因为调用BarCallback时不需要传参,函数的默认值会兜底。但问题在于,当这个函数被强制转换为FooCallback类型并传入boolean参数时,默认值会被覆盖,导致函数内部访问obj.param时出错。TypeScript在检查barImplementer赋值给BarCallback时,只验证了BarCallback的调用场景,无法追踪到后续类型转换后的风险。
内容的提问来源于stack exchange,提问作者Trevor
相关产品推荐
相关产品推荐

