Tanstack Table单元格定义类型不匹配问题求助
我近期正在学习React和TypeScript,尝试用Tanstack Table结合泛型定义可复用的复杂表格,但在展开自定义单元格定义函数时碰到类型错误。直接在列里定义cell函数能正常运行,但用useEditableCell函数返回的配置展开就报错,而且不能调整现有实现逻辑,求解决办法。
错误信息
类型为 '{ getUniqueValues?: AccessorFn<BulkEditRow, unknown[]> | undefined; footer?: ColumnDefTemplate<HeaderContext<BulkEditRow, unknown>> | undefined; ... 22 more ...; id: string; } | ... 6 more ... | { ...; }' 的参数无法赋值给类型 'IdentifiedColumnDef<BulkEditRow, number | null>'。
类型 '{ getUniqueValues?: AccessorFn<BulkEditRow, unknown[]> | undefined; footer?: ColumnDefTemplate<HeaderContext<BulkEditRow, unknown>> | undefined; ... 22 more ...; id: string; }' 无法赋值给类型 'IdentifiedColumnDef<BulkEditRow, number | null>'。
属性 'cell' 的类型不兼容。
类型 'ColumnDefTemplate<CellContext<BulkEditRow, unknown>> | undefined' 无法赋值给类型 'ColumnDefTemplate<CellContext<BulkEditRow, number | null>> | undefined'。
类型 '(props: CellContext<BulkEditRow, unknown>) => any' 无法赋值给类型 'ColumnDefTemplate<CellContext<BulkEditRow, number | null>> | undefined'。
类型 '(props: CellContext<BulkEditRow, unknown>) => any' 无法赋值给类型 '(props: CellContext<BulkEditRow, number | null>) => any'。
参数 'props' 和 'props' 的类型不兼容。
类型 'CellContext<BulkEditRow, number | null>' 无法赋值给类型 'CellContext<BulkEditRow, unknown>'。
这两个类型中 'cell.column.columnDef' 的类型不兼容。
类型 'ColumnDef<BulkEditRow, number | null>' 无法赋值给类型 'ColumnDef<BulkEditRow, unknown>'。
类型 'ColumnDefBase<BulkEditRow, number | null> & StringHeaderIdentifier' 无法赋值给类型 'ColumnDef<BulkEditRow, unknown>'。
类型 'ColumnDefBase<BulkEditRow, number | null> & StringHeaderIdentifier' 无法赋值给类型 'AccessorFnColumnDefBase<BulkEditRow, unknown> & IdIdentifier<BulkEditRow, unknown>'。
类型 'ColumnDefBase<BulkEditRow, number | null> & StringHeaderIdentifier' 中缺少属性 'accessorFn',但该属性在类型 'AccessorFnColumnDefBase<BulkEditRow, unknown> & IdIdentifier<BulkEditRow, unknown>' 中是必需的。ts(2345)
types.d.ts(83, 5): 'accessorFn' 在此处声明。
(property) IdentifiedColumnDef<BulkEditRow, number | null>.header?: ColumnDefTemplate<HeaderContext<BulkEditRow, number | null>> | undefined
相关代码
useEditableCell函数
export const useEditableCell = <TData extends object>( setTableState: React.Dispatch<React.SetStateAction<TableState>>, getTableState: TableState, customUpdateFunction?: CustomUpdateFunction, mode: 'View' | 'Edit' = 'Edit' ) => { const editableColumn = useMemo<Partial<ColumnDef<TData>>>(() => { return { cell: (cellProps) => { const isCurrentErrorCell = getTableState.errorLocation && cellProps.row.index === getTableState.errorLocation.rowIndex && cellProps.column.id === getTableState.errorLocation.columnId const cellValue = cellProps.getValue() const displayValue = cellValue === null ? '' : String(cellValue) if ( (getTableState.message != null && !isCurrentErrorCell) || mode === 'View' ) { return <TableDisplayCell value={displayValue} /> } return ( <EditableCell {...(cellProps as unknown as Cell<TData, unknown>)} updateData={ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion customUpdateFunction ?? cellProps.table.options.meta!.updateData } setTableState={setTableState} /> ) }, } }, [ getTableState.errorLocation, getTableState.message, mode, customUpdateFunction, setTableState, ]) return editableColumn }
使用方式
const editableCell = useEditableCell<BulkEditRow>( setTableState, getTableState )
列定义中使用
columnHelper.accessor('percentAustralia', { id: 'percentAustralia', header: () => RenderDefaultHeaderCell('% Australia'), size: 12, meta: { validate: validatePercentageValueInTable, }, ...editableCell, }),
直接定义cell的可行写法
cell: (cellProps) => { return <EditableCell {...(cellProps as unknown as Cell<TData, unknown>)} updateData={ // eslint-disable-next-line @typescript-eslint/no-non-null-assertion cellProps.table.options.meta!.updateData } setTableState={setTableState} /> }
解决办法
方案1:给useEditableCell增加值类型泛型(推荐)
修改useEditableCell的泛型定义,增加TValue参数来指定单元格的值类型,让返回的ColumnDef类型与目标列匹配:
export const useEditableCell = <TData extends object, TValue = unknown>( setTableState: React.Dispatch<React.SetStateAction<TableState>>, getTableState: TableState, customUpdateFunction?: CustomUpdateFunction, mode: 'View' | 'Edit' = 'Edit' ) => { const editableColumn = useMemo<Partial<ColumnDef<TData, TValue>>>(() => { return { cell: (cellProps) => { // 原有逻辑保持不变 const isCurrentErrorCell = getTableState.errorLocation && cellProps.row.index === getTableState.errorLocation.rowIndex && cellProps.column.id === getTableState.errorLocation.columnId const cellValue = cellProps.getValue() const displayValue = cellValue === null ? '' : String(cellValue) if ( (getTableState.message != null && !isCurrentErrorCell) || mode === 'View' ) { return <TableDisplayCell value={displayValue} /> } return ( <EditableCell {...(cellProps as unknown as Cell<TData, TValue>)} updateData={ customUpdateFunction ?? cellProps.table.options.meta!.updateData } setTableState={setTableState} /> ) }, } }, [ getTableState.errorLocation, getTableState.message, mode, customUpdateFunction, setTableState, ]) return editableColumn }
使用时显式指定值类型:
const editableCell = useEditableCell<BulkEditRow, number | null>( setTableState, getTableState )
方案2:展开时使用类型断言(不修改useEditableCell)
如果不想修改useEditableCell的实现,可在列定义展开时对editableCell做类型断言:
columnHelper.accessor('percentAustralia', { id: 'percentAustralia', header: () => RenderDefaultHeaderCell('% Australia'), size: 12, meta: { validate: validatePercentageValueInTable, }, ...(editableCell as Partial<ColumnDef<BulkEditRow, number | null>>), }),
原因说明
错误核心是useEditableCell默认返回的Partial<ColumnDef<TData>>中,cell函数的参数类型为CellContext<TData, unknown>,而目标列需要的是CellContext<TData, number | null>。由于TanStack Table的ColumnDef泛型参数不支持协变,TypeScript认为这两种上下文类型不兼容。通过显式指定值类型或类型断言,可让TypeScript正确识别类型兼容性。
内容的提问来源于stack exchange,提问作者Aevitas

