ReviewOS

home-lang/home

fix(ts): accept generic index values assignable to union string indexes (TS2413)

#953
Closed glennmichael123 opened this 7 days ago ·
7 days ago

TypeScript 5.7.3 emits one TS2542 for the readonly write in the following strict program; Home additionally emits two incorrect TS2413 diagnostics at the interface declarations.

interface Alpha<T> { make(): Beta<T> }
interface Beta<T> extends Alpha<T>, MixedBase<T> {}
interface MixedBase<T> { readonly [key: string]: T | (() => Beta<T>); [index: number]: T }
declare const alpha: Alpha<number>;
const beta = alpha.make();
beta["new"] = 2;
beta[0] = 2;
beta["0"] = 2;

TypeScript: TS2542 at line 6 only. Home after 9c0b6e2: TS2413 at lines 2 and 3, plus the expected TS2542 at line 6. The number index value T is assignable to the string index union T | (() => Beta<t>); recursion and forward declaration must not turn this into a false incompatibility. The numeric writes remain legal.

Reproduce from packages/ts_cli/testdata/issue846-mixed-index with --strict --noEmit --pretty false; Home also needs --ignoreConfig for the single-file run. This is distinct from #951: domain-specific readonly metadata now survives generic refresh, but the assignability relation still reports TS2413. Do not suppress TS2413 globally; establish a negative incompatible-index control and fix the underlying relation.

Related: #846, #950, #951, #952.

7 days ago

Further isolation: recursion is NOT required. A forward generic interface Derived<T> extends Base<T> {} with interface Base<T> { readonly [key: string]: T | (() => T); [index: number]: T } is clean under TypeScript 5.7.3 but Home reports false TS2413 at both declarations. Negative control interface InvalidIndexes<T> { [key: string]: T; [index: number]: () => T } reports one real TS2413 in both. Scope is the generic assignability relation from T to a union containing T; the recursive repro is one consequence. I have updated the issue title accordingly.

7 days ago

Fixed and pushed to origin/main in b8e0c64 and 3f35e62 (author/committer glennmichael123; no coauthors). Root cause: a union carrying T inherited the aggregate is_type_parameter flag, so heritageAssignableDepth treated the whole union as a single type parameter and rejected T before considering its union members. The relation now distinguishes actual type parameters from union/intersection composites and accepts a matching type-parameter union alternative. TypeScript 5.7.3 and Home agree on all three strict fixtures: compatible.ts clean; repro.ts one TS2542 at line 6; invalid.ts one real TS2413 at line 3. Checker, Program, and exact CLI regressions are committed. Focused checker/Program and test-tsc-streams exit 0; full Program exits 0. Full checker exits 0 but retains the same anomalous Zig test-runner failed-command output seen before this patch, so that broad result is qualified. All heavy runs used the 3,840 MB guard, one at a time. #951 is complete; #950 and #846 remain open.

Sign in to comment on this issue.