Skip to content

tsc silently drops ColumnDef type errors depending on file processing order (TypeScript variance bug) #6167

Description

@Faithfinder

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 reported

The 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't

The 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

Versions

  • @tanstack/react-table: 8.20.6
  • TypeScript: 5.9.3

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions