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

React+TypeScript复用多类型组件的类型问题求解

解决React+TS组件复用的类型识别问题

当使用FetchData | FetchData2联合类型时,TypeScript仅能访问两个类型共有的src字段,访问各自独有的name/title、price/item_price会触发类型错误——因为TS无法确定当前item属于哪一种类型。以下是几种可行的解决方法:

方法一:使用类型守卫(推荐)

定义类型守卫函数明确区分两种类型,让TS能正确推断字段:

import { useState, useEffect } from 'react';
import axios from 'axios';

interface FetchData {
  name: string;
  src: string;
  id: string;
  price: number;
}
interface FetchData2 {
  title: string;
  src: string;
  _id: string;
  item_price: number;
}

// 类型守卫:判断是否为FetchData类型
function isFetchData(item: FetchData | FetchData2): item is FetchData {
  return 'name' in item;
}

// 类型守卫:判断是否为FetchData2类型
function isFetchData2(item: FetchData | FetchData2): item is FetchData2 {
  return 'title' in item;
}

const Item = ({ item }: { item: FetchData | FetchData2 }) => {
  return (
    <li>
      <p>{isFetchData(item) ? item.name : item.title}</p>
      <img src={item.src} />
      <p>{isFetchData(item) ? item.price : item.item_price}</p>
    </li>
  );
};

const List = ({ type }: { type: 'foo' | 'bar' }) => {
  const [data, setData] = useState<FetchData[] | FetchData2[]>([]);

  useEffect(() => {
    const fetchData = async () => {
      if (type === 'foo') {
        const res = await axios.get<FetchData[]>('blablabla');
        setData(res.data);
      } else {
        const res = await axios.get<FetchData2[]>('blablabla');
        setData(res.data);
      }
    };
    fetchData();
  }, [type]);

  return (
    <>
      {data.map(item => (
        <Item 
          key={isFetchData(item) ? item.id : item._id} 
          item={item} 
        />
      ))}
    </>
  );
};

export default function App() {
  return (
    <ul>
      <List type="foo" />
      <List type="bar" />
    </ul>
  );
}

方法二:统一数据结构(更简洁)

在请求数据后,将两种格式的数据转换成统一结构,让Item组件仅接收单一类型:

import { useState, useEffect } from 'react';
import axios from 'axios';

// 定义统一的Item类型
interface UnifiedItem {
  title: string;
  src: string;
  id: string;
  price: number;
}

interface FetchData {
  name: string;
  src: string;
  id: string;
  price: number;
}
interface FetchData2 {
  title: string;
  src: string;
  _id: string;
  item_price: number;
}

// Item组件仅处理统一类型
const Item = ({ item }: { item: UnifiedItem }) => {
  return (
    <li>
      <p>{item.title}</p>
      <img src={item.src} />
      <p>{item.price}</p>
    </li>
  );
};

const List = ({ type }: { type: 'foo' | 'bar' }) => {
  const [data, setData] = useState<UnifiedItem[]>([]);

  useEffect(() => {
    const fetchData = async () => {
      let unifiedData: UnifiedItem[];
      if (type === 'foo') {
        const res = await axios.get<FetchData[]>('blablabla');
        unifiedData = res.data.map(item => ({
          title: item.name,
          src: item.src,
          id: item.id,
          price: item.price
        }));
      } else {
        const res = await axios.get<FetchData2[]>('blablabla');
        unifiedData = res.data.map(item => ({
          title: item.title,
          src: item.src,
          id: item._id,
          price: item.item_price
        }));
      }
      setData(unifiedData);
    };
    fetchData();
  }, [type]);

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

export default function App() {
  return (
    <ul>
      <List type="foo" />
      <List type="bar" />
    </ul>
  );
}

方法三:类型断言(快速但不推荐)

若能确保当前item的类型,可直接用类型断言,但判断错误会引发运行时问题:

const Item = ({ item }: { item: FetchData | FetchData2 }) => {
  return (
    <li>
      <p>{(item as FetchData).name || (item as FetchData2).title}</p>
      <img src={item.src} />
      <p>{(item as FetchData).price || (item as FetchData2).item_price}</p>
    </li>
  );
};

注意:原代码中axios.get是异步操作,直接调用map会报错(返回的是Promise而非数组),上述方案均已用useState+useEffect修正该问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 21:25:21