如何为元素值相关的对象创建TypeScript类型?
React表格数据的TypeScript强类型约束方案
要实现columns与rows的强关联约束,核心是通过泛型捕获列名字面量类型,同时强制列数组无重复且覆盖所有行对象的键。以下是具体实现方案:
1. 定义强类型表格Props
通过泛型T捕获列名的字面量类型,约束rows的每个对象必须包含所有T类型的键,同时通过readonly配合as const固定列名的字面量类型:
// 基础关联约束 type TableProps<T extends string> = { columns: readonly T[]; rows: readonly Record<T, unknown>[]; }; // 增强约束:列数组非空,且行对象不能包含列之外的额外键 type StrictTableProps<T extends string> = TableProps<T> & { columns: readonly [T, ...T[]]; // 确保至少有一列 rows: readonly (Record<T, unknown> & Record<Exclude<keyof rows[number], T>, never>)[]; };
2. React组件实现与类型推断
在组件中使用泛型,让TypeScript自动推断列名字面量类型,同时确保只有列中存在的键能合法访问行数据:
function DataTable<T extends string>({ columns, rows }: StrictTableProps<T>) { return ( <table> <thead> <tr> {columns.map(col => <th key={col}>{col}</th>)} </tr> </thead> <tbody> {rows.map((row, idx) => ( <tr key={idx}> {/* 合法:仅能使用columns中的键访问row */} {columns.map(col => <td key={col}>{row[col]}</td>)} {/* 非法:使用非columns中的键会触发TypeScript错误 */} {/* <td>{row['invalidKey']}</td> */} </tr> ))} </tbody> </table> ); }
3. 正确使用示例
传入columns时用as const固定字面量类型,TypeScript会自动校验rows结构是否匹配:
// 合法用法:columns与rows键完全匹配 const validColumns = ['name', 'age', 'email'] as const; const validRows = [ { name: 'Alice', age: 30, email: 'alice@example.com' }, { name: 'Bob', age: 25, email: 'bob@example.com' }, ]; <DataTable columns={validColumns} rows={validRows} />; // 非法用法1:rows缺少columns中的键(TS直接报错) const invalidRows1 = [ { name: 'Charlie', age: 35 }, // 缺少email字段 ]; // 非法用法2:rows包含columns外的额外键(TS直接报错) const invalidRows2 = [ { name: 'Dave', age: 40, email: 'dave@example.com', gender: 'male' }, ];
4. 进阶:强制columns无重复
如果需要严格约束列数组无重复,可添加辅助类型检查元素唯一性:
// 辅助类型:检查数组元素是否唯一 type IsUnique<T extends readonly any[]> = T extends [infer First, ...infer Rest] ? First extends Rest[number] ? never // 存在重复元素,返回never触发报错 : IsUnique<Rest> : true; // 增强版Props:强制列无重复 type StrictUniqueTableProps<T extends string> = StrictTableProps<T> & { columns: IsUnique<readonly T[]> extends true ? readonly T[] : never; };
使用该类型后,传入包含重复列名的columns会直接触发TypeScript错误。
内容的提问来源于stack exchange,提问作者frodetb
相关产品推荐
相关产品推荐

