mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-11-11 02:03:59 +08:00
TypeScript keeps the narrowing of a property reference (and of a binding that a closure assigns) across calls. After `c.cb = fn` or `if (c.name !== undefined)`, a callback, method or getter that clears or replaces the property leaves later reads typed by the old value. Lowering trusted that type: maybeNarrow extracted the union payload unchecked, and `?.`, truthiness and `typeof` folded by the narrowed type. A second `c.cb?.()` after a self-clearing callback threw where Node answers undefined, and `string | undefined`, `string | number` or record fields read after such a call crashed or reinterpreted memory. NarrowingStability decides whether a narrowing is provably current: every property in the reference is construction-only (no store to its name outside `this.name = ...` in a constructor body, no accessor, no computed or reflective store into instances) with a stable root, or nothing between the earliest occurrence of the reference in the use's function and the use can run other code or store the property through another reference (standard library calls without callbacks are inert; a loop around the use contributes its whole body). Proven reads keep the unchecked extraction. Otherwise the read observes the stored value. In `?.` receivers, absence, type and truthiness tests, member reads the declared type supports, and slots that accept the declared type, the reference reads as declared, so the consumer tests at runtime exactly as Node does. Everywhere else the extraction is checked and throws Node's TypeError for the consuming operation (`Cannot read properties of undefined (reading 'x')`, `o.f is not a function`). Calls through callback fields use the same message. A null-only receiver of a restored `?.` short-circuits to undefined, and `typeof` evaluates an effectful union operand once. The checked failure path groups arms by message so wide unions stay small: the self-hosted compiler's IR grows by 0.03%. tsc-ts self-check, Linux, interleaved in one VM: check time -1.5% (noise), `--checkers 8` -1.2%. Corpus 4650 pins the shapes; a unit test pins which reads stay unchecked.