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

React泛型Datatable组件TypeScript错误排查与解决咨询

泛型Datatable组件的TypeScript错误完整解决方案

问题概述

开发了一个接收动态rows和columns作为Props的泛型Datatable组件,使用泛型后遇到两个TypeScript错误:

  1. Property 'id' does not exist on type 'T'.ts(2339):组件中使用row.id作为<tr>的key,但泛型T未定义id属性
  2. Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'ReactNode':row[column.key]的类型无法确定是否符合React渲染要求

已知修改types.ts中的RowType为export type RowType<DataType> = DataType & {id?:number}可解决第一个错误,以下是完整解决方案。


原代码参考

App.tsx

import rows from "./data.json";

type ColumnKeys = keyof (typeof rows)[0];

export interface ColumnType {
  key: ColumnKeys; 
  label: string;
};


const columns: ColumnType[] = [
  { key: "id", label: "ID" },
  { key: "first_name", label: "First name" },
  { key: "last_name", label: "Last name" },
  { key: "email", label: "Email" },
  { key: "phone_no", label: "Phone No." },
  { key: "address", label: "Address" },
];

function App() {
  return (
    <div className="App">
      <Datatable
        {...{ columns, rows }}
      />
    </div>
  );
}

export default App;

types.ts

export type HeaderCell<DataType> = keyof DataType;

export type RowType<DataType> = DataType;

export interface ColumnType<DataType> {
  key: HeaderCell<DataType>;
  label: string;
}

Datatable.tsx

import React, { Key } from "react";
import "./Datatable.css";
import { ColumnType, HeaderCell, RowType } from "../type";

interface DatatableProps<DataType> {
  columns: ColumnType<DataType>[];
  rows: RowType<DataType>[];
}

function Datatable<T>({ columns, rows, sorting }: DatatableProps<T>)  {

  return (
    <>
      <table id="resizeMe" className="table">
        <thead className="table-header">
          <tr className="table-row">
            {columns.map((column, colIndex) => (
              <th
                key={column.key as Key}
              >
                <div className="table-header-data">
                  {column.label}
                </div>
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {rows.map((row) => {
            return (
              <tr
                key={row.id} // Property 'id' does not exist on type 'T'.ts(2339)
                className="table-row"
              >
                {columns.map((column) => (
                  <td
                    key={column.key as Key}
                  >
                    {row[column.key]} // Type 'T[string] | T[number] | T[symbol]' is not assignable to type 'ReactNode'.
                  </td>
                ))}
              </tr>
            );
          })}
        </tbody>
      </table>
    </>
  );
};

export default Datatable;

完整修改方案

1. 修改types.ts:约束行结构与列属性类型

import React from "react";

// 给行数据附加可选id属性,确保key有合法来源
export type RowType<DataType> = DataType & { id?: number };

// 表头cell的key必须是DataType的键
export type HeaderCell<DataType> = keyof DataType;

export interface ColumnType<DataType> {
  key: HeaderCell<DataType>;
  label: string;
}

2. 修改Datatable.tsx:完善泛型约束与类型安全

import React, { Key } from "react";
import "./Datatable.css";
import { ColumnType, RowType } from "../type";

// 约束数据类型的所有属性值都可作为React节点渲染
interface DatatableProps<DataType extends Record<string, React.ReactNode>> {
  columns: ColumnType<DataType>[];
  rows: RowType<DataType>[];
}

function Datatable<DataType extends Record<string, React.ReactNode>>({ columns, rows }: DatatableProps<DataType>) {
  return (
    <>
      <table id="resizeMe" className="table">
        <thead className="table-header">
          <tr className="table-row">
            {columns.map((column) => (
              <th key={column.key as Key}>
                <div className="table-header-data">{column.label}</div>
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          {rows.map((row) => {
            // 给key添加兜底值,避免id为undefined时出错
            return (
              <tr key={row.id ?? Math.random()} className="table-row">
                {columns.map((column) => (
                  <td key={column.key as Key}>
                    {row[column.key]}
                  </td>
                ))}
              </tr>
            );
          })}
        </tbody>
      </table>
    </>
  );
};

export default Datatable;

3. 修改App.tsx:对齐泛型类型,消除类型不匹配

import rows from "./data.json";
import Datatable from "./Datatable";
import { ColumnType } from "./types";

// 从rows自动推导行数据类型
type RowDataType = typeof rows[0];

// 使用泛型ColumnType,确保列key与行数据键完全匹配
const columns: ColumnType<RowDataType>[] = [
  { key: "id", label: "ID" },
  { key: "first_name", label: "First name" },
  { key: "last_name", label: "Last name" },
  { key: "email", label: "Email" },
  { key: "phone_no", label: "Phone No." },
  { key: "address", label: "Address" },
];

function App() {
  return (
    <div className="App">
      <Datatable<RowDataType> columns={columns} rows={rows} />
    </div>
  );
}

export default App;

修改说明

  1. 解决第一个错误:通过RowType<DataType> = DataType & { id?: number }为所有行数据附加可选id属性,同时在组件中给key添加兜底值(row.id ?? Math.random()),避免id为undefined时出现渲染错误。若业务要求id必填,可将id?: number改为id: number。

  2. 解决第二个错误:通过DataType extends Record<string, React.ReactNode>约束泛型DataType的所有属性值都属于React可渲染类型,让row[column.key]的类型被自动推断为ReactNode,完全符合React的渲染要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 06:05:13