ReviewOS

home-lang/home

fix(ts): resolve imported indexed-access mapped key domains

#669
Closed chris opened this 8 days ago ·
8 days ago

Parent: #548

Problem

After #666–#668, imported overloads, mapped-key identity, conditional handlers, nested structural Extract, and standard Extract<T, U> all transfer losslessly. An equivalent handler still collapses to implicit any when its mapped key domain is named through a source-owned indexed-access alias:

export interface A { kind: "a"; a: number }
export interface B { kind: "b"; b: string }
export type Item = A | B;
export type Kind = Item["kind"];
export type ItemOfKind<K extends Kind> = Extract<Item, { kind: K }>;
export type Handlers = { [K in Kind]?: (item: ItemOfKind<K>, rewritten: boolean) => Item };

The same reduction passes when Kind is spelled directly as "a" | "b". A nested _zod.def.type discriminant also passes with that literal domain. This isolates source-owned indexed-access alias resolution, not nested structural conditional matching.

Fair strict oracle

An imported overloaded visit(item, handler) / visit(item, handlers) call supplies property a. TypeScript 6.0.3 and the native preview infer callback item A and boolean, reporting only deliberate TS2322 plus two TS2339 controls. ReleaseFast Home emits two false TS7006 diagnostics and misses the controls.

This mirrors Zod 4.5.2's type Kind = schemas.$ZodTypeDef["type"] boundary without using its names or source layout.

Acceptance

  • Resolve a source-owned indexed access over a transferable object/union alias to its exact property domain before mapped-property admission and specialization.
  • Preserve literal union members, alias/type-parameter identity, optional mapped members, and #667 overload ambiguity rules.
  • Do not widen failed indexed accesses to string, any, or fabricated keys.
  • Add negative controls for a missing indexed property, an invalid object-literal property, unions with partial property coverage, nested discriminants, and generic/conditional handler use.
  • Add permanent schema/Program regressions and match the exact TypeScript 6.0.3/native oracle.
  • Run full Program/checker suites, ReleaseFast, formatting, whitespace, and required Pickier.
  • Run the unchanged Zod 4.5.2 admission graph twice, require zero diagnostic additions, and document exact removals or unchanged output.
  • Publish detailed docs plus the linked README benchmark table.

No Zod/package/path/name/property/line special cases, source-text matching, broad index widening, or callback-value-driven overload selection.

8 days ago

Implemented and pushed directly to main in two small, solely-authored conventional commits:

  • 12e1c1d6c fix(ts): resolve imported indexed key domains (#669)
  • 1ea1d2a7a test(ts): guard indexed key domain failures (#669)

The general fix resolves mapped constraints before attaching them to transported type parameters and routes union-object indexed accesses through the existing exact all-members resolver. Non-union objects retain the direct named-member path. No package, path, alias, property, source-text, or diagnostic special cases were added.

Permanent coverage now includes:

  • nested source-owned Item["_zod"]["def"]["type"] mapped key domains across a Program boundary;
  • imported overload + optional mapped member + distributive Extract specialization;
  • valid "a" / "b", invalid "c", callback member, and rewritten-boolean controls;
  • a direct no-widening guard proving complete unions produce "a" | "b", while missing members and partially covered unions return no exact result in both argument and infer paths.

Exact strict two-file oracle (module / moduleResolution: NodeNext, strict, noEmit):

EngineDiagnostics
TypeScript 6.0.32× TS2322, 2× TS2339
Native TypeScript 7 preview2× TS2322, 2× TS2339
ReleaseFast Home2× TS2322, 2× TS2339

All three accept "a" and "b", reject "c" against exactly "a" | "b", infer callback item A and rewritten value boolean, and emit no TS7006 or TS2345.

Pinned Zod 4.5.2 admission graph, two independent ReleaseFast runs:

  • 400 diagnostics / 395 unique path-line-column-code identities in both runs;
  • exact delta versus #668: 0 added, 0 removed;
  • both runs and the #668 parent stderr are byte-identical;
  • stderr SHA-256: 0590e3b2b593d2e2c0abb32f9f732d45f6d1701f3c2f906d07975ddf39c0bbc9;
  • normalized identity SHA-256 remains 86df9bf4b128d164b88a96d4dfa2441094dbeeaa514b2587fa73c5ac0f3cdab5.

Final gates:

  • Program suite: 195/195;
  • complete checker suite: pass (including a second pass after adding the no-widening guard);
  • focused schema, Program, and exact-indexed-access regressions: pass;
  • ReleaseFast build: pass;
  • zig fmt --check: pass;
  • git diff --check: pass;
  • repository-wide bunx --bun pickier .: unchanged existing baseline, 22,427 findings (11,834 errors, 10,593 warnings).

Timing is deliberately excluded: Zod still differs from TypeScript's zero-diagnostic oracle, and the shared host is contended. Documentation and the linked README table are the remaining #669 deliverable.

8 days ago

Completed on main.

  • implementation: 12e1c1d6c
  • no-widening guard: 1ea1d2a7a
  • detailed audit + linked README table: 15a508a11
  • production evidence: /home-lang/home/issues/669#issuecomment-5561267845

The exact TypeScript 6.0.3/native/Home oracle now matches at 2× TS2322 + 2× TS2339 with no TS7006/TS2345. Missing and partially covered indexed accesses are permanently guarded against widening. Zod remains deterministically unchanged at 400 diagnostics / 395 identities with zero additions, so no timing claim is made.

Sign in to comment on this issue.