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

TypeScript:如何为DataGrid列传递带正确类型的T[keyof T]?

React DataGrid 类型安全实现:解决列定义的联合类型问题

我在React项目中使用DataGrid组件,想实现类型安全,于是给数据定义了泛型T,给列定义了K extends keyof T。但创建列时TypeScript无法识别属性的具体类型,只会返回联合类型。

示例接口

interface DataGridProps<T extends object> {
    data: T[];
    columns: Array<ColumnProps<T, keyof T & string>>;
    idField: keyof T & string;
}
interface ColumnProps<T, K extends keyof T> {
    field: K,
    sorter?: (a: T[K], b: T[K]) => number;
    formatter?: (value: T[K]) => string;
}

尝试过的方法

  • 内联传递列数组(无效):
const gridProps: DataGridProps<Person> = {
  data: [{id: 1, name: "Test", active: true}],
  columns: [{
    field: "id",
    sorter: (a: number, b: number) => 1,
  }],
  idField: "id"
}
  • 创建列数组(无效):
const columns: ColumnProps<Person, keyof Person>[] =
  [{field: "id", sorter: (a: number, b: number) => 1}]

单独为每个列指定泛型是有效的,但我想知道有没有更简洁的解决方案?

const column1 : ColumnProps<Person, "id"> = {
  field: "id",
  sorter: (a: number, b: number) => 1,
}

解决方案

方法1:泛型辅助函数自动推断

直接定义数组类型时,TypeScript会将所有列的K合并为keyof T的联合类型。通过泛型函数包裹列的创建过程,可以让TypeScript逐个推断每个列的具体字段类型:

// 定义辅助函数
function createColumns<T>(...columns: Array<{ [K in keyof T]: ColumnProps<T, K> }[keyof T]>) {
  return columns;
}

// 使用示例
interface Person {
  id: number;
  name: string;
  active: boolean;
}

const columns = createColumns<Person>(
  { field: "id", sorter: (a, b) => a - b }, // a、b自动推断为number
  { field: "name", formatter: (value) => value.toUpperCase() }, // value自动推断为string
  { field: "active", formatter: (value) => value ? "启用" : "禁用" } // value自动推断为boolean
);

const gridProps: DataGridProps<Person> = {
  data: [{id: 1, name: "Test", active: true}],
  columns,
  idField: "id"
};

方法2:函数式组件自动推断

如果DataGrid是函数式组件,可以直接利用组件的泛型参数让TypeScript自动推导所有类型,无需额外辅助函数:

// 定义函数式DataGrid组件
function DataGrid<T extends object>(props: DataGridProps<T>) {
  // 组件逻辑实现
  return null;
}

// 使用时自动推断类型
DataGrid({
  data: [{id: 1, name: "Test", active: true}],
  columns: [
    { field: "id", sorter: (a, b) => a - b },
    { field: "name", formatter: (v) => v.toUpperCase() },
    { field: "active", formatter: (v) => v ? "是" : "否" }
  ],
  idField: "id"
});

方法3:修改DataGridProps的列类型定义

通过映射类型重新定义columns的类型,让每个列的K保持独立:

// 定义单个列的联合类型,每个列对应一个具体的K
type ColumnFor<T> = { [K in keyof T]: ColumnProps<T, K> }[keyof T];

interface DataGridProps<T extends object> {
    data: T[];
    columns: ColumnFor<T>[];
    idField: keyof T & string;
}

// 使用时,TypeScript会自动识别每个列的具体字段类型
const gridProps: DataGridProps<Person> = {
  data: [{id: 1, name: "Test", active: true}],
  columns: [
    { field: "id", sorter: (a, b) => a - b },
    { field: "name", formatter: (v) => v.toUpperCase() }
  ],
  idField: "id"
};

原理说明

当使用泛型函数或函数式组件时,TypeScript会对数组中的每个元素进行独立的类型推断,而非将所有列的字段类型合并为联合类型。这样既保证了类型安全,又无需手动为每个列指定具体的泛型参数,代码更简洁。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 04:36:59