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

通过关联参数类型区分TypeScript联合类型的实现问题

TypeScript TS7053错误解决:泛型关联枚举与类型

问题背景

先定义了以下枚举、接口与类型:

enum TableNames {
  clients = "clients",
  products = "products"
}

interface IClient {
  client: string;
  location: string;
  type: string;
}
interface IProduct {
  product: string;
  family: string;
  subfamily: string;
}

type TSelector<T> = {
  [K in keyof T]: T[K][];
};

type TClientSelector = TSelector<IClient>;
type TProductSelector = TSelector<IProduct>;

基于上述类型实现了初始化类和工厂函数:

export class BaseClientSelector implements TClientSelector {
  client: string[] = [];
  type: string[] = [];
  location: string[] = [];
}

export class BaseProductSelector implements TProductSelector {
  product: string[] = [];
  family: string[] = [];
  subfamily: string[] = [];
}

const getEmptySelectorObject = (name: TableNames): TClientSelector | TProductSelector => {
  if (name === TableNames.clients) {
    return new BaseClientSelector();
  } else {
    return new BaseProductSelector();
  }
};

在实现getSelectorData时遇到TS7053错误:

export const getSelectorData = (
  arrayOfObj: (IClient | IProduct)[],
  name: TableNames
): TClientSelector | TProductSelector => {
  const selectorData: TClientSelector | TProductSelector = getEmptySelectorObject(name);

  for (const col in selectorData) {
    selectorData[col] = Array.from(
//  ^^^^^^^^^^^^^^^^^ TS7053: 元素隐式具有 "any" 类型,因为类型为 "string" 的表达式不能用于索引类型 "TClientSelector | TProductSelector"
      new Set(arrayOfObj.map((obj: IClient | IProduct) => obj[col]))
//                                                        ^^^^^^^^ TS7053: 元素隐式具有 "any" 类型,因为类型为 "string" 的表达式不能用于索引类型 "IClient | IProduct"
    ).sort((a, b) => String(a).localeCompare(String(b)));
  }

  return selectorData;
};

问题原因

TypeScript无法建立name参数与selectorData具体类型的关联关系:

  • 遍历selectorData得到的col被推断为string类型,无法直接索引联合类型TClientSelector | TProductSelector
  • arrayOfObj是联合类型数组,用string类型的col索引时,TypeScript无法确认该键在所有联合成员中存在

尝试过类型守卫会导致代码重复,用any会丢失类型安全,函数重载灵活性不足,需要更优雅的泛型方案。

泛型解决方案

通过创建枚举到实体类型的映射,结合泛型让TypeScript自动推断类型关联:

1. 定义枚举与类型的映射

// 建立TableNames到对应实体类型的映射
type TableToEntity = {
  [TableNames.clients]: IClient;
  [TableNames.products]: IProduct;
};

// 根据枚举类型推导对应的选择器类型
type TableToSelector<T extends TableNames> = TSelector<TableToEntity[T]>;

2. 泛化工厂函数

修改getEmptySelectorObject为泛型函数,让返回类型与输入的枚举值关联:

const getEmptySelectorObject = <T extends TableNames>(name: T): TableToSelector<T> => {
  if (name === TableNames.clients) {
    return new BaseClientSelector() as TableToSelector<T>;
  } else {
    return new BaseProductSelector() as TableToSelector<T>;
  }
};

3. 实现类型安全的getSelectorData

用泛型约束参数之间的类型关系,解决索引问题:

export const getSelectorData = <T extends TableNames>(
  arrayOfObj: TableToEntity[T][],
  name: T
): TableToSelector<T> => {
  const selectorData = getEmptySelectorObject(name);

  // 将Object.keys断言为当前实体类型的键数组,避免string类型问题
  (Object.keys(selectorData) as Array<keyof TableToEntity[T]>).forEach(col => {
    selectorData[col] = Array.from(
      new Set(arrayOfObj.map(obj => obj[col]))
    ).sort((a, b) => String(a).localeCompare(String(b)));
  });

  return selectorData;
};

方案优势

  • 保持了完全的类型安全,没有使用any
  • 避免了类型守卫带来的代码重复
  • 泛型自动推断类型关联,调用时会根据传入的name自动约束arrayOfObj的类型,返回对应类型的选择器对象

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 09:35:36