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
相关产品推荐
相关产品推荐

