-
-
Notifications
You must be signed in to change notification settings - Fork 3.5k
Description
Description
tsc silently accepts incorrect type assignments involving ColumnDef<TData, TValue> that the IDE (VS Code / language server) correctly flags as errors. The root cause is a known TypeScript variance computation bug with mutually recursive generic types (microsoft/TypeScript#44572).
The ColumnDef and Column types are mutually recursive, and when TypeScript's variance computation hits the cycle, it bails out and caches TValue as [independent] — making ColumnDef<Row, string> incorrectly assignable to ColumnDef<Row, unknown>. Whether this happens depends on file processing order (alphabetical glob expansion).
Reproduction
Repo: https://github.com/Faithfinder/ts-ide-cli-repro (main branch uses @tanstack/react-table, standalone branch is zero-dependency)
git clone https://github.com/Faithfinder/ts-ide-cli-repro.git
cd ts-ide-cli-repro
pnpm install
npx tsc --noEmit # Exit code 0 — errors silently dropped (BUG)Rename the trigger file so it sorts after test.ts:
mv src/_trigger.ts src/zzz_trigger.ts
npx tsc --noEmit # Exit code 1 — errors correctly reportedThe test file contains assignments that should always error:
import type { ColumnDef } from "@tanstack/react-table";
type Row = { name: string; age: number };
declare function useTable(columns: ColumnDef<Row>[]): void;
declare const columnsUnion: (ColumnDef<Row, string> | ColumnDef<Row, number>)[];
useTable(columnsUnion); // Should error — IDE flags it, tsc doesn't
declare const columnsString: ColumnDef<Row, string>[];
useTable(columnsString); // Should error — IDE flags it, tsc doesn'tThe trigger file just imports Column in a way that causes the variance cycle to be computed before test.ts is processed:
import type { RowData, Column } from "@tanstack/react-table";
export const f = <TData extends RowData>(cols: Column<TData, unknown>[]) => {
cols.map((col) => col.id);
};Root cause
This is TypeScript's variance computation cycle-handling bug: microsoft/TypeScript#44572 (closed as Design Limitation). A fix was attempted in microsoft/TypeScript#48080 but rejected due to performance regression.
The --generateTrace output shows getVariancesWorker computing different variance results for ColumnDefBase's TValue depending on file order.
Suggested fix
TypeScript 4.7 introduced variance annotations (in/out/in out) specifically as the workaround for this class of bugs (microsoft/TypeScript#48240). Adding explicit variance annotations to ColumnDef, Column, and related mutually recursive types would establish correct variance regardless of file processing order.
This would also resolve the long-standing confusion in #4241 and #4382, where Tanner couldn't reproduce the ColumnDef type errors locally (likely due to different file ordering in different projects).
Related issues
- columnHelper.accessor Type issue #4241 — columnHelper.accessor Type issue (maintainer couldn't reproduce — check-order sensitivity)
- ColumnDef types gives typescript error #4382 — ColumnDef types gives typescript error (61 comments, open)
- Check order dependence with mutually-recursive non-unary generics microsoft/TypeScript#44572 — Check order dependence with mutually-recursive non-unary generics
- tsc reports error that LS does not microsoft/TypeScript#39811 — tsc reports error that LS does not
Versions
@tanstack/react-table: 8.20.6- TypeScript: 5.9.3