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

如何用TypeScript泛型实现多类型列表项的通用列表组件

解决TypeScript泛型在通用表格行组件中的类型匹配问题

你的问题核心在于TypeScript无法推断出Row组件和当前item的类型是严格匹配的——原来的getRowComponentByType返回的是联合类型组件,TypeScript会默认要求data属性同时满足所有联合类型的交集(也就是Cat & Dog),这显然不符合实际场景。

下面是重构后的完整解决方案,通过类型映射让TypeScript能精准关联实体类型和对应的行组件:

1. 定义类型与组件的映射关系

首先明确所有实体类型和其__typename的对应关系,同时建立组件映射表:

import React from 'react';

// 假设你的实体类型定义如下(根据实际情况调整)
type Cat = {
  __typename: 'Cat';
  id: string;
  name: string;
  meowVolume: number;
};

type Dog = {
  __typename: 'Dog';
  id: string;
  name: string;
  barkVolume: number;
};

// 1. 定义实体类型映射表:将__typename和对应的实体类型绑定
type EntityTypeMap = {
  Cat: Cat;
  Dog: Dog;
  // 后续新增的实体类型都在这里添加
};

// 2. 提取所有合法的__typename类型
type EntityTypeName = keyof EntityTypeMap;

// 3. 定义行组件的通用类型
type RowComponent<K extends EntityTypeName> = React.FC<{ data: EntityTypeMap[K] }>;

// 4. 实现具体的行组件
const CatRow: RowComponent<'Cat'> = ({ data }) => (
  <tr>
    <td>{data.name}</td>
    <td>{data.meowVolume}</td>
  </tr>
);

const DogRow: RowComponent<'Dog'> = ({ data }) => (
  <tr>
    <td>{data.name}</td>
    <td>{data.barkVolume}</td>
  </tr>
);

// 5. 组件映射表:__typename -> 对应的行组件
const rowComponentMap: { [K in EntityTypeName]: RowComponent<K> } = {
  Cat: CatRow,
  Dog: DogRow,
};

2. 实现类型安全的组件获取函数

替换原来的getRowComponentByType,让TypeScript能根据输入的__typename精准推断返回的组件类型:

function getRowComponentByType<K extends EntityTypeName>(typename: K): RowComponent<K> {
  return rowComponentMap[typename];
}

3. 重构通用Rows组件

利用泛型和类型映射,确保列表类型和行组件类型严格匹配:

interface RowsProps<K extends EntityTypeName> {
  list: EntityTypeMap[K][];
}

const Rows = <K extends EntityTypeName>({ list }: RowsProps<K>) => {
  if (list.length === 0) {
    // 空列表提示,注意添加colSpan避免表格布局错乱
    return <tr><td colSpan="100%">暂无数据</td></tr>;
  }

  // 从第一个元素的__typename推断当前列表的实体类型
  const typename = list[0].__typename as K;
  const Row = getRowComponentByType(typename);

  return (
    <>
      {list.map((item) => (
        <Row key={item.id} data={item} />
      ))}
    </>
  );
};

export default Rows;

4. 使用示例

在业务组件中直接传入对应类型的列表即可,TypeScript会自动校验类型:

const CatList = () => {
  const { list, total } = useQuery<Cat[]>(/* 你的数据查询逻辑 */);
  
  return (
    <BaseTable>
      <Rows list={list} />
    </BaseTable>
  );
};

const DogList = () => {
  const { list, total } = useQuery<Dog[]>(/* 你的数据查询逻辑 */);
  
  return (
    <BaseTable>
      <Rows list={list} />
    </BaseTable>
  );
};

关键优化点

  • 类型映射表:通过EntityTypeMap让TypeScript明确__typename和实体类型的绑定关系,避免联合类型的模糊推断
  • 精准组件推断:getRowComponentByType利用泛型约束,返回与输入__typename严格对应的组件类型
  • 自动类型校验:业务组件传入列表时,TypeScript会自动校验列表类型是否在EntityTypeMap中,提前发现类型错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 00:50:13