Files
scriptc/tests/diagnostics/runtime-optional-refined-union.ts
Chris Tate 92642d775e Read holes in for-of loops and missing-element shorthand properties as undefined
A for-of loop over an array whose own facts show a hole or a stored
undefined binds T | undefined, and the typed parameters, locals, record
fields and Math arguments it reaches widen to hold it. Loops over arrays
the element-state analysis proves free of holes read each element
directly, without a per-element state test.

Object literals built from a missing-element read keep the property as
undefined in shorthand and long form, through spreads, nested records,
arrays of records, annotated locals and literal arguments to typed
parameters.
2026-10-09 22:05:36 -05:00

39 lines
1.2 KiB
TypeScript

// Structural refinements still need a named fence for multi-value storage.
// The class refinement below is accepted through a checked identity-preserving view.
type Item = { rect?: { width: number } };
function hasRect(item: Item | string): item is Item & { rect: { width: number } } {
return typeof item !== "string" && item.rect !== undefined;
}
function recordWidths(items: (Item | string)[]): void {
for (const item of items) {
if (hasRect(item)) console.log(item.rect.width);
}
}
class Animal {
name: string;
constructor(name: string) { this.name = name; }
}
class Dog extends Animal {
bark(): string { return this.name; }
}
function isDog(animal: Animal | string): animal is Dog {
return typeof animal !== "string" && animal instanceof Dog;
}
function classNames(animals: (Animal | string)[]): void {
for (const animal of animals) {
if (isDog(animal)) console.log(animal.bark());
}
}
recordWidths([{ rect: { width: 1 } }, "plain"]);
classNames([new Dog("dog"), "plain"]);
// A hole keeps the loop binding optional, so its refined read meets the fence.
const pending: (Item | string)[] = ["plain"];
pending[2] = { rect: { width: 2 } };
recordWidths(pending);