TypeScript为何允许回调函数不声明全部非可选参数?
TypeScript回调函数参数个数兼容性问题
问题场景
我在TypeScript中实现了一个类似Array.prototype.map()的字典处理函数mapDict,函数运行正常:
// Array.prototype.map, but for Dict function mapDict<T, M>(dict: Dict<T>, callback: (dictValue: T, dictKey: string, dict: Dict<T>) => M): Dict<M> { const mappedDict: Dict<M> = {} for (let keyValPair of Object.entries(dict)) { const mappedVal: M = callback(keyValPair[1], keyValPair[0], dict); mappedDict[keyValPair[0]] = mappedVal; } return mappedDict; };
但存在一个疑惑:函数声明中明确指定第二个参数是接收三个非可选参数的回调函数,但调用时传入只声明一个参数的回调,TypeScript却没有报错:
const fruits = { apple: { color: "red", mass: 100 }, grape: { color: "red", mass: 5 }, banana: { color: "yellow", mass: 183 }, lemon: { color: "yellow", mass: 80 }, pear: { color: "green", mass: 178 }, orange: { color: "orange", mass: 262 }, raspberry: { color: "red", mass: 4 }, cherry: { color: "red", mass: 5 }, } interface Dict<T> { [k: string]: T } const mappedDict = mapDict(fruits, (fruit) => { // return { // ...fruit, // kg: 0.001 * fruit.mass // } });
我猜测TypeScript实际检查的是调用回调时传入的参数数量是否符合类型定义,而非回调自身声明的参数数量,希望得到专业解答。
解答
这是TypeScript函数类型兼容性的核心规则之一:当一个函数类型被用来作为另一个函数类型的实现时,允许接收更少的参数。
具体来说,mapDict声明的回调类型要求能接收三个参数,但你传入的回调只声明了第一个参数,这在TypeScript中完全合法,原因如下:
- 从调用安全角度看:
mapDict内部调用回调时会传入三个参数,但回调如果只接收第一个,剩下的参数会被自动忽略,不会引发运行时错误——JavaScript本身就允许函数忽略多余传入的参数。 - 从类型系统设计角度看:TypeScript的函数兼容性遵循参数个数协变,即如果目标函数需要N个参数,那么提供一个接收≤N个参数的函数是安全的。反过来,如果目标函数只需要1个参数,你传入一个要求3个参数的函数就会报错,因为调用时只会传1个,会导致参数缺失的问题。
举个简单例子,以下代码同样合法:
type Callback3 = (a: number, b: string, c: boolean) => void; const fn1: Callback3 = (a) => { console.log(a) }; // 合法 const fn2: Callback3 = (a, b) => { console.log(a, b) }; // 合法 const fn3: Callback3 = () => {}; // 合法
而如果反过来:
type Callback1 = (a: number) => void; const fn4: Callback1 = (a, b, c) => { console.log(a, b, c) }; // 报错,因为Callback1调用时只会传1个参数,fn4要求3个会导致参数未定义
你的猜测方向是对的,TypeScript关注的是调用时传入的参数是否能被回调处理,而不是回调声明了多少参数——只要调用时提供的参数满足回调的参数要求(回调接收的参数个数≤调用时传入的个数),就不会报错。
内容的提问来源于stack exchange,提问作者MrRobot333
相关产品推荐
相关产品推荐

