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

如何在不使用any类型的前提下重塑对象适配复杂TypeScript接口?

TypeScript类型安全转换Item到NewItem的解决方案

我尝试调用transformData()方法转换Item结构以适配NewItem接口,但触发了如下TypeScript错误:

TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type NewItem
No index signature with a parameter of type string was found on type NewItem

我知道问题出在:用string类型的键去索引NewItem类型的对象,而NewItem没有定义string类型的索引签名。但我不能给NewItem加[key: string]: number索引(因为还有其他非number类型的属性),也不想用any或关闭严格模式,希望在保留类型安全的前提下解决问题。

原触发错误的简化代码

interface Item {
  itemName: ItemName;
  quantity: number;
  itemBreakdown?: [];
}

type ItemName = 'Dog' | 'Plant' | 'Unknown'

interface NewItem {
  animal: number;
  plant: number;
  food: number;
  unknown: number;
  itemBreakdown?: {
    category1?: [ItemDetails]
    category2?: [ItemDetails]
  };
  error?: Error | Error[]
}

private transformData(items: Item[]): NewItem {
  const newItem: NewItem = {};

  items.reduce((acc, item) => {
    const { itemName, quantity } = item;
    // 错误触发行
    acc[this.mapItemName(itemName)] = quantity;
    return acc;
  }, newItem);

  return newItem;
}

private mapItemName(itemName: string): string {
  switch (itemName) {
    case 'Dog':
      return 'animal';
    case 'Plant':
      return 'plant';
    default:
      return 'unknown';
  }
}

解决方法

核心思路是让TypeScript明确mapItemName返回的键是NewItem中合法的数字类型属性,而非宽泛的string类型,同时修正NewItem的初始化问题。

  1. 修正mapItemName的类型定义

    • 参数类型改为ItemName(因为传入的itemName本身就是这个联合类型,不需要接受任意string)
    • 返回类型明确为'animal' | 'plant' | 'unknown'(也就是NewItem中对应ItemName转换后的合法键),让TypeScript能识别这是合法的索引键。
  2. 正确初始化NewItem
    NewItem的animal、plant、food、unknown都是必填的number属性,不能直接用{}初始化,需要给这些属性赋初始值(比如0)。

修改后的完整代码

interface Item {
  itemName: ItemName;
  quantity: number;
  itemBreakdown?: [];
}

type ItemName = 'Dog' | 'Plant' | 'Unknown'

interface NewItem {
  animal: number;
  plant: number;
  food: number;
  unknown: number;
  itemBreakdown?: {
    category1?: [ItemDetails]
    category2?: [ItemDetails]
  };
  error?: Error | Error[]
}

// 提前定义转换后的键类型,方便复用
type MappedNewItemKey = 'animal' | 'plant' | 'unknown';

private transformData(items: Item[]): NewItem {
  // 初始化所有必填属性为0
  const newItem: NewItem = {
    animal: 0,
    plant: 0,
    food: 0,
    unknown: 0
  };

  items.reduce((acc, item) => {
    const { itemName, quantity } = item;
    const key = this.mapItemName(itemName);
    acc[key] = quantity;
    return acc;
  }, newItem);

  return newItem;
}

private mapItemName(itemName: ItemName): MappedNewItemKey {
  switch (itemName) {
    case 'Dog':
      return 'animal';
    case 'Plant':
      return 'plant';
    case 'Unknown':
      return 'unknown';
    default:
      // 用never类型确保覆盖所有ItemName分支,避免遗漏
      const _exhaustiveCheck: never = itemName;
      throw new Error(`Unsupported itemName: ${_exhaustiveCheck}`);
  }
}

额外说明

  • 新增的MappedNewItemKey类型让转换后的键更清晰,也方便后续维护。
  • 新增的默认分支用never类型做穷尽检查:如果后续新增了ItemName的枚举值,TypeScript会直接报错,提醒你更新mapItemName的逻辑,进一步保障类型安全。

内容的提问来源于stack exchange,提问作者Chun Chan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 20:52:05