← 返回导航

表格列定义类型

业务类型 · 实战

场景说明

后台管理系统中,表格列配置几乎是每个列表页都要写的东西: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;
}

关键点解析

使用示例与类型推导


      
// 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'
在 Playground 中尝试