后台管理系统中,表格列配置几乎是每个列表页都要写的东西:prop 绑定行数据字段、label 显示表头名、width 列宽、align 对齐方式、formatter 自定义渲染、fixed 固定列、sortable 是否可排序。用泛型把列配置和行数据类型绑定起来,prop 字段就只能是行数据的 key,formatter 的 row 参数也自动推导出正确类型,彻底告别拼错字段名、记错字段类型的低级错误。
// 表格列定义类型:泛型绑定行数据类型,prop 受 keyof 约束
// formatter 自动推导 row 类型,字段名错误编译期就暴露
// 1. 对齐方式字面量联合
type ColumnAlign = 'left' | 'center' | 'right';
// 2. 固定列方向
type ColumnFixed = 'left' | 'right';
// 3. 排序方向
type SortOrder = 'ascending' | 'descending' | null;
// 4. 表格列配置:泛型 T 绑定行数据类型
interface TableColumn<T = any> {
prop: keyof T; // 列属性:必须是行数据的 key
label: string; // 表头显示名
width?: number | string; // 列宽
minWidth?: number; // 最小列宽
align?: ColumnAlign; // 对齐方式
fixed?: ColumnFixed | boolean; // 固定列
sortable?: boolean; // 是否可排序
hidden?: boolean; // 是否隐藏
formatter?: (row: T, rowIndex: number) => string | number; // 自定义渲染
}
// 5. 表格配置
interface TableOptions<T> {
columns: TableColumn<T>[];
data: T[];
border?: boolean;
stripe?: boolean;
height?: number | string;
}
// 6. 行数据示例:用户列表
interface UserRow {
id: number;
name: string;
email: string;
age: number;
status: number; // 0 禁用 1 启用
createdAt: string;
}
// 7. 定义列:prop 只能是 UserRow 的 key
const userColumns: TableColumn<UserRow>[] = [
{
prop: 'id',
label: 'ID',
width: 80,
align: 'center',
fixed: 'left',
sortable: true,
},
{
prop: 'name',
label: '用户名',
minWidth: 120,
},
{
prop: 'email',
label: '邮箱',
minWidth: 200,
},
{
prop: 'age',
label: '年龄',
width: 100,
align: 'right',
sortable: true,
},
{
prop: 'status',
label: '状态',
width: 100,
align: 'center',
formatter: (row) => row.status === 1 ? '启用' : '禁用',
},
{
prop: 'createdAt',
label: '创建时间',
minWidth: 180,
formatter: (row) => new Date(row.createdAt).toLocaleString(),
},
];
// 8. 渲染表格列(示意)
function renderHeaderCell<T>(col: TableColumn<T>): string {
return col.label;
}
function renderBodyCell<T>(row: T, col: TableColumn<T>, rowIndex: number): string | number {
if (col.formatter) {
return col.formatter(row, rowIndex);
}
return row[col.prop] as string | number;
}
TableColumn<T>:列配置的泛型参数 T 与行数据类型绑定,prop 字段用 keyof T 约束,写错字段名 TS 直接报错,是表格列类型设计的核心。'left' | 'center' | 'right'、fixed 用 'left' | 'right' | boolean,比 string 安全得多,传入非法值编译期拦截。// prop 拼写错误会被 TS 拦截
const badColumns: TableColumn<UserRow>[] = [
// { prop: 'usrname', label: '用户名' } // 错误:'usrname' 不在 UserRow key 中
];
// formatter 中 row 的类型自动推导
const col: TableColumn<UserRow> = {
prop: 'status',
label: '状态',
formatter: (row) => {
// row: UserRow,row.status 自动提示
return row.status === 1 ? '启用' : '禁用';
}
};
// 订单列表复用同一套列类型
interface OrderRow {
orderNo: string;
amount: number;
payStatus: number;
}
const orderColumns: TableColumn<OrderRow>[] = [
{ prop: 'orderNo', label: '订单号', minWidth: 180 },
{ prop: 'amount', label: '金额', align: 'right', sortable: true },
];
// 列 prop 的联合类型推导
type UserColumnProps = TableColumn<UserRow>['prop'];
// => 'id' | 'name' | 'email' | 'age' | 'status' | 'createdAt'