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

如何用TypeScript泛型约束React列表组件仅接受ItemOne或ItemTwo类型项

解决方法

1. 定义联合类型

先把ItemOne和ItemTwo合并成一个联合类型,作为泛型约束的基础:

import { ItemOne, ItemTwo } from './your-path'; // 替换成实际导入路径

export type ListItemType = ItemOne | ItemTwo;

2. 约束泛型参数

修改组件的泛型约束,让T只能是ListItemType的子类型(也就是仅接受ItemOne或ItemTwo):

type Props<T extends ListItemType> = {
  items: T[];
};

const MyList = <T extends ListItemType>({ items }: Props<T>) => {
  // 后续逻辑
};

3. 添加类型守卫函数

为了让TypeScript正确区分两种类型,需要定义类型守卫函数,明确判断当前item的类型:

// 利用ItemOne独有的id字段判断类型
const isItemOne = (item: ListItemType): item is ItemOne => {
  return 'id' in item;
};

// 若type字段有固定枚举值,也可以用type判断(比如ItemOne的type为'Type One')
// const isItemOne = (item: ListItemType): item is ItemOne => {
//   return item.type === 'Type One';
// };

4. 修正组件渲染逻辑

在遍历列表时,用类型守卫判断item类型,让TypeScript能正确推断id或uuid的存在:

const MyList = <T extends ListItemType>({ items }: Props<T>) => {
  return (
    <ul css={styles.list}>
      {items.map((item) => (
        <MyListItem
          key={isItemOne(item) ? item.id : item.uuid}
          title={item.title}
        />
      ))}
    </ul>
  );
};

完整代码示例

import { ItemOne, ItemTwo } from './your-path';
import MyListItem from './MyListItem';
import styles from './styles';

export type ListItemType = ItemOne | ItemTwo;

type Props<T extends ListItemType> = {
  items: T[];
};

const isItemOne = (item: ListItemType): item is ItemOne => {
  return 'id' in item;
};

const MyList = <T extends ListItemType>({ items }: Props<T>) => {
  return (
    <ul css={styles.list}>
      {items.map((item) => (
        <MyListItem
          key={isItemOne(item) ? item.id : item.uuid}
          title={item.title}
        />
      ))}
    </ul>
  );
};

export default MyList;

修改完成后,组件的items属性仅能接受ItemOne[]、ItemTwo[]或两者混合的数组,传入不符合类型的数组时TypeScript会直接抛出错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 21:36:29