为什么 Array.map(f) 会推断出 number[][]?
结论
map 不会因为回调返回数组就额外“包裹”结果。它总会返回一个新数组,里面包含每次回调的返回值。题目中的 number[][] 表示 TypeScript 检查 [1, 2, 3].map(f) 时,将回调的返回类型推断为 number[],所以 map 的返回类型是 number[][]。运行时,回调 f 接收的是单个数字,最终结果仍是一维数字数组。
map 的类型如何形成
map 的类型关系可以简化为:输入元素类型是 T,回调返回类型是 U,整个调用的返回类型就是 U[]。因此,要看传给 map 的回调被推断为返回什么类型:
- 回调返回
number时,map返回number[]。 - 回调返回
number[]时,map返回number[][]。
题目中的 f 有两个重载:传入单个数字时返回数字,传入数字数组时返回数字数组。直接把这个泛型重载函数作为回调时,TypeScript 会检查它是否兼容 map 所需的回调类型,并进行类型推断。这里推断出的回调返回类型与显式调用 f(v) 时不同,因此多出了一层数组类型。
具体选择哪个重载以及泛型如何推断,可能受 TypeScript 版本和完整类型上下文影响。题目没有提供版本信息,因此不能把这个现象归结为某条固定的内部规则。可以确定的是,number[][] 是静态推断结果,不代表运行时的 map 会把每个数字转换成数组。
推荐写法:显式包装调用
用箭头函数明确地把当前数字传给 f:
const result = [1, 2, 3].map((v) => f(v))
// result: number[]
这里 f(v) 接收单个数字并返回数字,map 汇总这些返回值后得到 number[]。如果只是对数组中的每个数字执行相同运算,也可以直接写成:
const result = [1, 2, 3].map((v) => 2 * v)
// result: number[]
注意事项
类型推断是编译器对表达式的静态判断,不会改变 JavaScript 的运行时行为。把带有多个泛型重载的函数直接作为回调传入时,如果得到的类型不符合预期,可以用箭头函数显式调用来消除歧义。要确认特定项目中的推断细节,可以结合该项目使用的 TypeScript 版本检查最小复现代码。
备注:内容仅供参考。
