Tree-shakeable feature typing dilemma in v9 (react) #6535
Replies: 1 comment
|
The reason none of your patterns work is that this isn't a matter of finding the right constraint. Reproduced on function A<TFeatures extends TableFeatures, TData extends RowData>({
table,
}: {
table: Table<TFeatures, TData>
}) {
return table.store.state.sorting
// ^ Property 'sorting' does not exist on type
// '{} | (TableState_CellSelection & … & TableState_RowSorting)'
}Look at the type in that error: a union of That is also why tightening the bound changes nothing: TFeatures extends TableFeatures & Required<Pick<TableFeatures, 'rowSortingFeature'>>
// identical errorThe union comes from the deferred conditional, not from the constraint, so no bound on There is a second thing worth knowing: That third parameter is the way out. import type {
ReactTable,
RowData,
TableFeatures,
TableState_RowSorting,
} from '@tanstack/react-table'
export function SortingAware<
TFeatures extends TableFeatures,
TData extends RowData,
TState extends TableState_RowSorting,
>({ table }: { table: ReactTable<TFeatures, TData, TState> }) {
return table.state.sorting // resolved — no assertion
}Checked against tables with different feature sets: const small = tableFeatures({ rowSortingFeature, columnVisibilityFeature })
const big = tableFeatures({ rowSortingFeature, columnVisibilityFeature, rowSelectionFeature })
SortingAware({ table: useTable({ features: small, columns, data }) }) // ok
SortingAware({ table: useTable({ features: big, columns, data }) }) // ok
const noSorting = tableFeatures({ columnVisibilityFeature })
SortingAware({ table: useTable({ features: noSorting, columns, data }) })
// Type 'ReactTable<…, TableState_ColumnVisibility>' is not assignable to
// 'ReactTable<…, TableState_RowSorting>'Any feature set containing sorting is accepted, the ones without it are rejected at the call site, and nothing is asserted — which is what you were after. For the components that take a export function ColumnSortingAware<TFeatures extends TableFeatures, TData extends RowData>({
column,
}: {
column: Column<TFeatures, TData, unknown> & Column_RowSorting<TFeatures, TData>
}) {
return column.getIsSorted()
}
// call site — accepted:
// table.getAllColumns().map((column) => <ColumnSortingAware column={column} />)
|
Uh oh!
There was an error while loading. Please reload this page.
I'm currently struggling a bit with the migration from v8 to v9 in a React project. This is related to the newly introduced dependency of the whole typing system on the tableFeatures that have been selected when creating the table instance with useTable.
Background:
We use quite a few reusable components that take e.g. table or column objects as input, interact with the table, column, etc. API(s) and render stuff as a result of these API calls.
There might be a sorting-aware component that uses information about the sorting state of column(s) and a selection-aware component that accesses information about selected rows. These components can then be used for different all kinds of tables that use different sets of features. In the past, typing was easy, because all features were imported by default, so all APIs were always present on each table, column, ... instance.
Problem in the migration process:
When typing e.g. a table input prop of such a component as { table: Table<TFeatures extends TableFeatures, TData extends RowData>}, by default I will not be able to use most of the table API methods, because TableFeatures is pretty bare-bones. That means, I somehow need to narrow the type to make sure typescript understands that the e.g. sorting API methods are available in the component code. At the same time, I don't want to type the table too specifically for a certain set of features because I want to use the component for a table that has e.g. the feature sets {rowSortingFeature, columnVisibilityFeature}, {rowSortingFeature, rowSelectionFeature, columnVisibilityFeature} and all kinds of conceivable feature combinations.
I've tried some typing patterns in the following codesandbox (see file src/SortingAware.tsx): https://codesandbox.io/p/devbox/polished-hazelnut-vktpys?file=%2Fsrc%2FSortingAware.tsx
None of them really works, and my only current workarounds are really nasty type assertions (which sometimes work, sometimes not but can get easily out of hands for complex components). Am I overlooking something really obvious here? What is a clean pattern for achieving safe and consistent typing?
All reactions