TypeScript泛型组件报错:every属性不存在、参数隐式any类型
TypeScript泛型组件类型推断问题
问题描述
我是TypeScript泛型新手,尝试将page.tsx中通过react-query获取的数据传递给泛型组件header.tsx时,遇到两个TypeScript错误:
- 类型
T不存在属性every - 参数
record隐式具有any类型
我确认传递的数据是数组且已存在,猜测只要让header.tsx识别data属性的类型,TypeScript就能自动推断出相关类型。
相关代码
types.ts
export type ProductRow = { id: string; name?: string; sku?: string; };
page.tsx
import { useState } from 'react'; import { Header } from './header'; import { ProductRow } from './types'; export const Page = () => { // 模拟react-query useQuery获取的数据 const data = [ { brand: { id: 'clywipo320000cayabq4iub7e', name: 'Nero', minMargin: 30, maxMargin: 40, }, id: '1', name: "100mm Round Tile Insert Floor Waste 50mm Outlet Brushed Gold", priceRecords: [ { price: 120, date: 'Fri Aug 23 2024 10:00:00 GMT+1000 (Australian Eastern Standard Time)' } ], sku: 'NRFW003GD' }, { brand: { id: 'clywipo320000cayabq4iub7e', name: 'Nero', minMargin: 30, maxMargin: 40, }, id: '2', name: "100mm Round Tile Insert Floor Waste 50mm Outlet Brushed Bronze", priceRecords: [ { price: 112, date: 'Fri Aug 23 2024 10:00:00 GMT+1000 (Australian Eastern Standard Time)' } ], sku: 'NRFW003BZ' } ] const [selectedRows, setSelectedRows] = useState<ProductRow[]>([]); return ( <div> <Header data={data} selectedRows={selectedRows} /> { data.map(record => <input type={`checkbox`} checked={selectedRows.map(row => row.id).includes(record.id)} onClick={() => setSelectedRows(prevValue => [...prevValue, { id: record.id, name: record.name, sku: record.sku }])} /> )} </div> ) }
header.tsx
import { ProductRow } from './types'; type HeaderProps<T> = { data: T, selectedRows: ProductRow[] }; export const Header = <T,>({ data, selectedRows }: HeaderProps<T>) => { return ( <div className={``}> <div className={`flex gap-3 items-start`}> <input type={`checkbox`} checked={data.every(record => selectedRows.map(row => row.id).includes(record.id))} /> <p>SKU</p> </div> <p className={`col-span-3`}>Product Name</p> <p>Brand</p> <p className={`text-center`}>RRP</p> <p className={`text-center`}>Max Discount (%)</p> <p className={`text-center`}>Buildmat Price</p> <p className={`text-center`}>TheBlueSpace Price</p> </div> ) };
解决方案
问题核心是泛型T没有被约束为数组类型,TypeScript无法识别data是数组,因此不知道它有every方法,也无法推断record的类型。需要给泛型T添加数组约束,同时指定数组元素至少包含id属性(因为代码中用到了record.id):
修改后的header.tsx
import { ProductRow } from './types'; // 约束T为包含id属性的对象数组 type HeaderProps<T extends { id: string }[]> = { data: T, selectedRows: ProductRow[] }; export const Header = <T extends { id: string }[]>({ data, selectedRows }: HeaderProps<T>) => { return ( <div className={``}> <div className={`flex gap-3 items-start`}> <input type={`checkbox`} checked={data.every(record => selectedRows.map(row => row.id).includes(record.id))} /> <p>SKU</p> </div> <p className={`col-span-3`}>Product Name</p> <p>Brand</p> <p className={`text-center`}>RRP</p> <p className={`text-center`}>Max Discount (%)</p> <p className={`text-center`}>Buildmat Price</p> <p className={`text-center`}>TheBlueSpace Price</p> </div> ) };
如果需要更严谨的类型定义,可以在types.ts中添加数据元素的具体类型:
export type Product = { id: string; name?: string; sku?: string; brand: { id: string; name: string; minMargin: number; maxMargin: number; }; priceRecords: { price: number; date: string; }[]; };
然后在header.tsx中使用该类型约束泛型:
import { ProductRow, Product } from './types'; type HeaderProps<T extends Product[]> = { data: T, selectedRows: ProductRow[] }; export const Header = <T extends Product[]>({ data, selectedRows }: HeaderProps<T>) => { // 组件内容不变 };
修改后,TypeScript能正确推断data是数组类型,record的类型也会被识别,两个错误会自动消失。
内容的提问来源于stack exchange,提问作者Jamie Nguyen
相关产品推荐
相关产品推荐

