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

如何为React+TypeScript可复用表格组件编写合适的接口?

React + TypeScript 可复用表格组件的接口设计方案

问题描述

我正在用 React 和 TypeScript 开发表格组件,想实现完全复用,让它能接收服务端返回的任意结构数据作为 props,适配不同的列和表头。

一开始我给组件数据定义了简单接口:

interface IData {
  id: number;
}

但实际数据结构差异很大,比如待办事项(todos)有title: string、body: string字段,用户(users)则包含surname: string、name: string、email: string等字段,这说明组件 props 得支持任意数据结构。

另外,我还希望组件能接收自定义的 React 元素,比如复选框、删除行按钮这类操作元素。

现在我有两个待完善的接口,不知道怎么修改:

// 这里想放任意 React 节点
interface IActions {

}
interface ITable extends IActions{
  headers: string[];
  body: I[];
}

解决方案

1. 用泛型实现任意数据兼容

核心思路是给表格组件和接口加上泛型,让它能适配任意类型的行数据,同时通过列配置实现自定义表头、内容和操作元素。

先定义完整的类型接口:

import React from 'react';

// 列配置:支持自定义表头、字段映射或自定义渲染
interface TableColumn<T> {
  // 表头显示文字
  header: string;
  // 可选:直接映射行数据的字段名(自动取对应值渲染)
  field?: keyof T;
  // 可选:自定义内容渲染函数(优先级高于 field,支持操作按钮、状态转换等)
  render?: (row: T) => React.ReactNode;
}

// 表格完整 props 接口
interface TableProps<T> {
  // 列配置数组(替代单纯的 headers,更灵活)
  columns: TableColumn<T>[];
  // 表格数据:泛型 T 对应任意行数据类型
  data: T[];
  // 可选:额外的全局操作(比如批量选择栏、导出按钮)
  extraActions?: React.ReactNode;
}

2. 实现泛型表格组件

基于上面的接口编写组件,兼容直接取字段和自定义渲染两种场景:

const Table = <T extends Record<string, unknown>>({ columns, data, extraActions }: TableProps<T>) => {
  return (
    <table border={1} cellPadding={8} cellSpacing={0}>
      {/* 表头 */}
      <thead>
        <tr>
          {columns.map((col, idx) => (
            <th key={idx}>{col.header}</th>
          ))}
        </tr>
      </thead>
      {/* 表体 */}
      <tbody>
        {data.map((row, rowIdx) => (
          <tr key={rowIdx}>
            {columns.map((col, colIdx) => (
              <td key={colIdx}>
                {/* 优先用自定义渲染,无则直接取字段值 */}
                {col.render ? col.render(row) : row[col.field!]}
              </td>
            ))}
          </tr>
        ))}
      </tbody>
      {/* 额外操作栏 */}
      {extraActions && (
        <tfoot>
          <tr>
            <td colSpan={columns.length}>{extraActions}</td>
          </tr>
        </tfoot>
      )}
    </table>
  );
};

export default Table;

3. 实际使用示例

针对不同数据类型,只需传入对应的数据和列配置即可:

待办事项表格

// 定义待办事项类型
interface Todo {
  id: number;
  title: string;
  completed: boolean;
}

const TodoTable = () => {
  const todos: Todo[] = [
    { id: 1, title: '学习 TypeScript 泛型', completed: false },
    { id: 2, title: '完成可复用表格组件', completed: true },
  ];

  // 列配置:包含状态转换和操作按钮
  const columns: TableColumn<Todo>[] = [
    { header: 'ID', field: 'id' },
    { header: '标题', field: 'title' },
    { header: '状态', render: row => row.completed ? '✅ 已完成' : '🔘 未完成' },
    { header: '操作', render: row => (
      <button onClick={() => console.log('删除待办:', row.id)}>删除</button>
    )}
  ];

  return <Table columns={columns} data={todos} />;
};

用户表格

// 定义用户类型
interface User {
  id: number;
  surname: string;
  name: string;
  email: string;
}

const UserTable = () => {
  const users: User[] = [
    { id: 1, surname: '张', name: '三', email: 'zhangsan@example.com' },
    { id: 2, surname: '李', name: '四', email: 'lisi@example.com' },
  ];

  // 列配置:自定义姓名拼接、复选框操作
  const columns: TableColumn<User>[] = [
    { header: '姓名', render: row => `${row.surname}${row.name}` },
    { header: '邮箱', field: 'email' },
    { header: '操作', render: row => (
      <div>
        <input type="checkbox" onChange={() => console.log('选中用户:', row.id)} />
        <button onClick={() => console.log('编辑用户:', row.id)}>编辑</button>
      </div>
    )}
  ];

  return <Table columns={columns} data={users} />;
};

核心优势

  • 完全复用:通过泛型T适配任意行数据结构,无需为每种数据单独编写组件或接口
  • 类型安全:TypeScript 会自动校验列配置与数据的匹配性,避免字段名写错等低级错误
  • 高度灵活:支持直接映射字段、自定义内容渲染、添加操作元素等多种场景

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 07:15:51