Capítulo 21 de 54
tableFeatures, tableOptions, and createColumnHelper add zero runtime behavior — they exist purely to keep TypeScript inference intact across the feature set / row type / column defs / options relationship, so use them wherever type safety matters, and skip tableOptions when a plain object works fine.
tableFeatures(): use for every v9 table, even tableFeatures({}). What goes in it: feature objects (rowSortingFeature, custom features), row-model factories (createSortedRowModel(), ...), function registries (filterFns/sortFns/aggregationFns), and type-only meta slots (tableMeta/columnMeta, usually via metaHelper). Registering a function under a name (sortFns: { byRank: ... }) makes that string a valid, type-checked sortFn/filterFn/aggregationFn value on column defs — an unregistered name is a type error.tableOptions(): composes reusable table option fragments while keeping generic inference connected — returns the same object at runtime, its only value is the TypeScript overload. Use it for shared defaults across many tables (e.g. a defaultColumn size range + debugTable flag spread into several useTable calls), or when a wrapper/factory supplies data/columns/features later and a fragment (e.g. initialState) needs to type-check against the eventual feature set and row type via explicit tableOptions<typeof features, Person>({...}). Skip it for one-off tables — just pass the object straight to useTable.createColumnHelper<TFeatures, TData>(): returns .accessor() (data columns), .display() (display-only columns), .group() (nested column groups), and .columns([...]) (wraps an array while preserving each column's individual inferred value type — prefer this over a bare array literal for anything with grouped/nested columns). At runtime it only returns plain column-def objects; its value is entirely in inference precision, especially for accessor return types, feature-specific column options, and nested groups. Plain ColumnDef object literals remain valid when the extra inference isn't needed.// tableFeatures: named sort function becomes a type-checked string option
const features = tableFeatures({
rowSortingFeature,
sortedRowModel: createSortedRowModel(),
sortFns: { byRank: (a, b, id) => a.getValue<number>(id) - b.getValue<number>(id) },
})
const columnHelper = createColumnHelper<typeof features, Person>()
columnHelper.accessor('rank', { sortFn: 'byRank' }) // 'byRank' is type-checked against sortFns
// tableOptions: shared defaults across multiple tables
const sharedOptions = tableOptions({
features: tableFeatures({}),
defaultColumn: { minSize: 80, maxSize: 400 },
debugTable: true,
})
const table = useTable({ ...sharedOptions, columns, data })
tableFeatures into a column def, and a reusable options fragment spread into a concrete table call.tableFeatures(), even for a features-free table — tableFeatures({}) is the explicit, correct way to say "core only."tableOptions() only when options are genuinely shared or composed across steps/tables; a one-off table doesn't need it.createColumnHelper earns its keep on accessor functions, nested groups, and feature-specific column options — for simple flat columns, a plain ColumnDef[] is equally valid.tableFeatures().createColumnHelper output in full detail.metaHelper, the phantom-slot pattern used alongside tableFeatures.