ReviewOS

home-lang/home

fix(ts): preserve FlatArray element types for readonly union arrays

#973
Open glennmichael123 opened this 1 day ago ·
1 day ago

Related #960, #846, #66.

TypeScript 5.7.3 control (--noEmit --strict --target es2023 --skipLibCheck):

declare const values: readonly (number[] | string[])[];
const flattened = values.flat();
const numbers: number[] = flattened;
const booleans: boolean[] = flattened;

TypeScript reports TS2322 for both assignments because flattened is (string | number)[]. Fresh Home home-tsc exits 0 with no diagnostics for the same source. Both runs were supervised under the 3,840 MB guard.

packages/ts_checker/src/lib.zig currently sets the synthetic flat() result to any[] whenever the element type is a union. Its own comment says this is a fallback for missing distributive flattening. This loses type information and accepts impossible assignments. The same implementation models only default depth 1, so explicit depth and nested arrays need a matched oracle too.

Acceptance: reproduce this as a repository TypeScript 5.7.3 exact-stream fixture for readonly and mutable receivers, then implement the underlying FlatArray result semantics for union elements and supported depth forms without a permissive any[] fallback. Preserve array callback/member behavior, run focused and full checker, Program, LSP, and the expanded CLI stream audit under the memory guard. Keep #960 and #66 updated.

1 day ago

The matched #973 oracle exposed a second, independent import-side error, now tracked in #974: JSDoc ReadonlyArray<number[] | string[]> was projected as an array of number[] | string because a postfix [] was stripped before the top-level union split. Both root fixes and a combined checked-JavaScript CLI fixture are in the working checkout; guarded verification is in progress before any commit.

1 day ago

Progress (not closed): the working checkout now distributes flat()'s default-depth result over number[] | string[] rather than widening it to any[]. A focused checker unit test covers (number | string)[], and a combined admitted-JavaScript fixture checks readonly and mutable receivers in both checkJs modes. The fixture also exposed #974 (JSDoc union precedence), whose parser fix is in the checkout, and #975 (TypeScript 5.7.3 union diagnostic order depends on skipLibCheck).

A matched TS 5.7.3 control confirms four intentional TS2322 errors in the fixture. Home's last built CLI has the correct semantic types and four errors, but its nested union explanation is one line short and the imported union order differs. A focused checker patch for the missing child is added but not yet rebuilt; host memory is below the agreed 40% Zig build admission gate. Explicit flat(depth) forms remain open in this issue. No commit or parity claim yet.

1 day ago

Pushed 801194f82 to origin/main as Glenn, without coauthors. It fixes the nested TS2322 explanation when every member of an array element union fails assignment; the focused regression and full checker suite passed under the 3,840 MB guard. The rebuilt CLI now matches TypeScript's nested chain in both checkJs modes.

The default-depth flat() result and #974 JSDoc precedence changes remain uncommitted pending full Program/LSP verification and the separate #975 union display-order gap. An explicit flat(2) control still shows a real semantic gap: TypeScript returns number[] from readonly number[][][], while Home returns number[][]. #973 stays open.

1 day ago

Additional matched semantic control found a flaw in the uncommitted flat() implementation, so it must not be pushed yet:

interface ArrayLikeOnly { length: number; [index: number]: number }
declare const values: readonly ArrayLikeOnly[];
const numeric: number[] = values.flat();
const original: ArrayLikeOnly[] = values.flat();

TypeScript 5.7.3 emits TS2322 only for numeric: FlatArray tests Arr extends ReadonlyArray<infer InnerArr>, and this indexed object is not a ReadonlyArray. The current Home binary emits TS2322 only for original, because packages/ts_checker/src/lib.zig treats any numeric index as proof that the element is flattenable. This is a real semantic inversion, separate from #975's display ordering and the explicit flat(2) depth gap. The fix needs an array/readonly-array relation that distinguishes actual arrays from indexed objects; an objectNumberIndex or [number] + length shape check is insufficient.

1 day ago

Pushed 4be9ed980 and b214ad08c to main. Default-depth flat() now distributes over array unions using array/tuple provenance, and preserves indexed objects such as ArrayLikeOnly instead of flattening them. Imported Program array types retain their origin, and incompatible union-array assignments include the element explanation. The new flat.ts oracle has the same five diagnostic sites and explanations as TypeScript 5.7.3 in both checkJs modes.

Guarded full checker, Program, and LSP suites passed. The expanded admitted-JavaScript exact-stream audit is 153/155: its two mismatches are only the imported union display order in the new fixture, tracked by #975. This issue remains open for explicit depth (flat(2)) and fuller ReadonlyArray conditional-type semantics.

22 hours ago

Matched TypeScript 5.7.3 control for the remaining FlatArray semantics:

interface Arrayish extends ReadonlyArray<number> { tag: string }
declare const nested: readonly number[][][];
const depth0: number[][][] = nested.flat(0);
const depth1: number[][] = nested.flat();
const depth2: number[] = nested.flat(2);
const wrong2: string[] = nested.flat(2);
declare const structural: readonly Arrayish[];
const structuralFlat: number[] = structural.flat();

Under --noEmit --strict --skipLibCheck --target es2023, TypeScript reports only the intentional TS2322 on wrong2 (line 6). Fresh Home reports extra TS2322s on depth0, depth2, and structuralFlat, and describes wrong2 as number[][] rather than number[]. This confirms that the remaining work includes depth-sensitive FlatArray evaluation and the extends ReadonlyArray relation for interfaces, beyond the array-origin check shipped in #973's default-depth batch. No patch for these cases is committed yet.

22 hours ago

The #960 structural control narrows the FlatArray blocker: ReadonlyIndexed with only readonly length and [index: number] is accepted by Home as ReadonlyArray<number>, but TypeScript 5.7.3 rejects that assignment with TS2740 for missing array methods. In contrast, Arrayish extends ReadonlyArray<number> is accepted by TypeScript and is flattened by flat(), while Home currently leaves Arrayish intact. See the #960 control and exact diagnostics. The fix needs a complete ReadonlyArray relation before using it to decide whether FlatArray<Arr, D> unwraps a user-defined element type.

Sign in to comment on this issue.