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
相关产品推荐
相关产品推荐

