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

React 18升级后ItemsList组件TypeScript类型错误求助

React 18升级后ItemsList组件的TypeScript类型错误解决

问题场景

升级至React 18后,使用ItemsList组件时触发TypeScript类型错误,组件调用代码如下:

<ItemsList items={items} dataTestId='items_list_block'>
  {(item) => ( // 报错位置
    <ItemView
      item={item}
    />
  )}
</ItemsList>

ItemsList组件定义:

type tItemsListProps<T extends tOptionListItem> = {
  children: (item: T) => React.ReactNode;
  items: T[];
  dataTestId?: string;
};

function ItemsList<T extends tOptionListItem>({
  children,
  items,
  dataTestId,
}: React.PropsWithChildren<tItemsListProps<T>>) {
  return (
    <Box>
      <List>
        {items?.map((item) => (
          <List.Item key={item.id} data-testid='list_item'>
            {children(item)}
          </List.Item>
        ))}
      </List>
    </Box>
  );
}

错误信息

TS2322: Type '(item: T) => Element' is not assignable to type '(((item: T) => ReactNode) & string) | (((item: T) => ReactNode) & number) | (((item: T) => ReactNode) & false) | (((item: T) => ReactNode) & true) | (((item: T) => ReactNode) & ReactElement<...>) | (((item: T) => ReactNode) & ReactFragment) | (((item: T) => ReactNode) & ReactPortal)'.
Type '(item: T) => Element' is not assignable to type '((item: T) => ReactNode) & string'.
Type '(item: T) => Element' is not assignable to type 'string'.
ItemsList.tsx(7, 3): The expected type comes from property 'children' which is declared here on type 'IntrinsicAttributes & tItemsListProps & { children?: ReactNode; }'

问题原因

React 18中React.PropsWithChildren会自动为props添加children?: ReactNode属性,而你自定义的children: (item: T) => React.ReactNode与这个默认属性类型冲突——默认的ReactNode包含字符串、数字、布尔值等多种类型,导致TypeScript无法正确推导回调函数的类型。

解决方法

方法1:移除React.PropsWithChildren直接使用自定义Props

既然已经在tItemsListProps中明确定义了children的类型,无需再用PropsWithChildren包裹,避免额外添加冲突的children类型:

function ItemsList<T extends tOptionListItem>({
  children,
  items,
  dataTestId,
}: tItemsListProps<T>) { // 移除PropsWithChildren
  return (
    <Box>
      <List>
        {items?.map((item) => (
          <List.Item key={item.id} data-testid='list_item'>
            {children(item)}
          </List.Item>
        ))}
      </List>
    </Box>
  );
}

方法2:重命名回调类型的children属性

如果需要保留PropsWithChildren(比如组件还需支持其他类型的children),可以将作为渲染回调的children重命名为renderItem这类明确名称:

type tItemsListProps<T extends tOptionListItem> = {
  renderItem: (item: T) => React.ReactNode; // 重命名属性
  items: T[];
  dataTestId?: string;
};

function ItemsList<T extends tOptionListItem>({
  renderItem,
  items,
  dataTestId,
  children, // 保留默认children用于其他内容渲染
}: React.PropsWithChildren<tItemsListProps<T>>) {
  return (
    <Box>
      <List>
        {items?.map((item) => (
          <List.Item key={item.id} data-testid='list_item'>
            {renderItem(item)}
          </List.Item>
        ))}
      </List>
      {children} {/* 可选:渲染其他子内容 */}
    </Box>
  );
}

调用方式修改为:

<ItemsList 
  items={items} 
  dataTestId='items_list_block' 
  renderItem={(item) => <ItemView item={item} />}
/>

方法3:覆盖PropsWithChildren的默认children类型

通过交叉类型明确覆盖默认的children类型,确保TypeScript识别正确的回调类型:

function ItemsList<T extends tOptionListItem>({
  children,
  items,
  dataTestId,
}: tItemsListProps<T> & { children?: never }) { // 覆盖默认children为never
  return (
    <Box>
      <List>
        {items?.map((item) => (
          <List.Item key={item.id} data-testid='list_item'>
            {children(item)}
          </List.Item>
        ))}
      </List>
    </Box>
  );
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 14:05:17