TypeScript中Array.reduce返回错误类型,是否为类型推断Bug?
这不是 TypeScript 的 Bug,是 Array.reduce 的泛型推断规则和 TypeScript 结构类型系统、最佳公共类型推导共同作用的正常表现。
核心原理
首先看 Array.reduce 的核心泛型签名:
interface Array<T> { reduce<U>( callback: (prev: U, curr: T, index: number, arr: T[]) => U, initialValue: U ): U }
TS 会自动从两个位置推断泛型参数 U 的类型:
- 传入的初始值
initialValue的类型 - 回调函数返回值的类型
最终会取两个类型的最佳公共类型作为U的实际类型。
场景1:MyItem.value 为必填属性
此时 MyItem 的结构是:
interface MyItem { id?: string; value: string; // 必填 } const myItemList: MyItem[] = [];
调用 reduce 时:
const myMap = myItemList.reduce((accum, item) => ({ ...accum, [item.id!]: item }), {});
- 初始值类型是
{} - 回调返回的是
{ [键: string]: MyItem }结构的对象
因为 MyItem 存在必填属性value,{}不符合 MyItem 的类型要求,无法和回调返回值的类型收敛出更具体的公共类型,所以最终U被推断为初始值的{}类型,符合预期。
场景2:MyItem.value 改为可选属性
此时 MyItem 的所有属性都是可选的:
interface MyItem { id?: string; value?: string; // 改为可选 } const myItemList: MyItem[] = [];
这种情况下 TS 的类型判断会发生两个变化:
- 空对象
{}完全符合 MyItem 的类型要求(所有属性可选,不需要存在任何属性) - 回调返回的对象虽然多了自定义的键值对,但 TypeScript 采用结构子类型判断,只有直接传入对象字面量赋值时才会检查多余属性,如果是匹配已有类型,只要对象已存在的属性符合类型要求、缺失的属性都是可选的,就算有多余属性也会被认为符合类型。也就是说你回调返回的对象完全可以被判定为符合 MyItem 类型。
所以此时初始值和回调返回值的最佳公共类型就是 MyItem,TS 自然会把 reduce 的返回值类型推导为 MyItem。
解决方案
如果要得到预期的字典类型,直接给 reduce 显式指定泛型参数即可,跳过自动推断:
// 显式指定返回值为 Record<string, MyItem> 类型 const myMap = myItemList.reduce<Record<string, MyItem>>((accum, item) => ({ ...accum, [item.id!]: item }), {});
内容的提问来源于stack exchange,提问作者asinkxcoswt
相关产品推荐
相关产品推荐

