You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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 的类型:

  1. 传入的初始值 initialValue 的类型
  2. 回调函数返回值的类型
    最终会取两个类型的最佳公共类型作为 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 的类型判断会发生两个变化:

  1. 空对象 {} 完全符合 MyItem 的类型要求(所有属性可选,不需要存在任何属性)
  2. 回调返回的对象虽然多了自定义的键值对,但 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 13:54:02