Typescript Material-UI Table因Data接口差异的重复代码复用问题求解
改造方案
1. 定义通用泛型类型
核心思路是利用TypeScript泛型让组件不绑定固定的数据接口,支持任意传入的数据结构类型。首先定义可复用的基础类型:
// 通用表头单元格类型,泛型T对应当前表格的数据结构 interface HeadCell<T extends object> { id: keyof T; label: string; numeric?: boolean; disablePadding?: boolean; } // 泛型化的EnhancedTableProps,不再绑定固定的Data接口 interface EnhancedTableProps<T extends object> { classes: ReturnType<typeof useStyles>; numSelected: number; onRequestSort: (event: React.MouseEvent<unknown>, property: keyof T) => void; onSelectAllClick: (event: React.ChangeEvent<HTMLInputElement>) => void; order: 'asc' | 'desc'; orderBy: keyof T; rowCount: number; // 表头配置作为props传入,不同表格传专属配置即可 headCells: HeadCell<T>[]; }
2. 改造EnhancedTableHead为泛型组件
原有渲染逻辑完全不需要修改,仅需要给组件加上泛型声明:
function EnhancedTableHead<T extends object>(props: EnhancedTableProps<T>) { const { classes, onSelectAllClick, order, orderBy, numSelected, rowCount, onRequestSort, headCells } = props; const createSortHandler = (property: keyof T) => (event: React.MouseEvent<unknown>) => { onRequestSort(event, property); }; return ( <TableHead> <TableRow> <TableCell padding="checkbox"> <Checkbox indeterminate={numSelected > 0 && numSelected < rowCount} checked={rowCount > 0 && numSelected === rowCount} onChange={onSelectAllClick} inputProps={{ 'aria-label': '全选当前页项' }} /> </TableCell> {headCells.map((headCell) => ( <TableCell key={String(headCell.id)} align={headCell.numeric ? 'right' : 'left'} padding={headCell.disablePadding ? 'none' : 'normal'} sortDirection={orderBy === headCell.id ? order : false} > <TableSortLabel active={orderBy === headCell.id} direction={orderBy === headCell.id ? order : 'asc'} onClick={createSortHandler(headCell.id)} > {headCell.label} {orderBy === headCell.id ? ( <span className={classes.visuallyHidden}> {order === 'desc' ? '已降序排序' : '已升序排序'} </span> ) : null} </TableSortLabel> </TableCell> ))} </TableRow> </TableHead> ); }
3. 业务场景使用示例
针对不同页面的不同数据结构,仅需要定义对应的数据接口和表头配置即可,组件逻辑100%复用:
// 示例:用户管理页的表格数据结构 interface UserData { id: number; name: string; email: string; createTime: string; } // 对应表头配置 const userHeadCells: HeadCell<UserData>[] = [ { id: 'id', label: '用户ID', numeric: true }, { id: 'name', label: '用户名', disablePadding: true }, { id: 'email', label: '联系邮箱' }, { id: 'createTime', label: '注册时间' }, ]; // 页面中直接使用组件 <EnhancedTableHead<UserData> classes={classes} numSelected={selectedIds.length} order={order} orderBy={orderBy} onSelectAllClick={handleSelectAll} onRequestSort={handleSort} rowCount={userList.length} headCells={userHeadCells} />
补充说明
- TypeScript会自动做类型校验,如果表头配置的id、排序字段不在当前数据结构的字段范围内,会直接抛出类型错误,保证类型安全
- 大部分场景下不需要手动传入泛型参数,TS会根据传入的
headCells自动推断对应的数据结构类型 - 后续如果要扩展表格能力,也只需要在泛型类型中增加对应属性即可,所有使用的业务页面都可以无缝适配
内容的提问来源于stack exchange,提问作者Rinkashikachi
相关产品推荐
相关产品推荐

