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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 07:47:03