Files
scriptc/tests/diagnostics/destructuring-fences.ts
Chris Tate 385b0a9199 Promote destructuring assignment targets, class-instance sources, class rest, and accessor defaults to the static tier
- Destructuring assignment now takes nested patterns (`({ p: { q } } = src)`, `[[a], b] = t`) and property/element targets (`[c.x, box.vals[i]] = arr`, `({ a: rec.f } = o)`, rest into members) — receivers temp at their pattern position, JS's reference-before-read order; defaults on member targets test against the target's own type. Defaults on nested targets keep a named fence (no single binding type to test against).
- Class instances destructure in assignment position like they already did in declarations: field slots and getter calls per element via the same fieldGetExpr dispatch, defaults, renames, and member targets; methods and undeclared names keep their pointed fences.
- Rest over class-instance sources packs the remaining instance FIELDS into a fresh record (JS's CopyDataProperties copies own enumerable properties — never prototype methods/accessors, matching the checker's rest type), in declarations and assignments; non-public fields, runtime-provided chains (Error/EventEmitter/stream), and key orders the packed shape cannot reproduce fence instead of diverging.
- Defaults on destructured getter results now lower (record and class accessors alike), with the getter result temped so the default's undefined test never calls it twice; numeric-literal pattern keys (`const { 2: x } = o`) fold to JS's canonical property name.
- Corpus 2536-2540 pin the new forms byte-identically against Node on both backends; the destructuring-fences diagnostics program keeps the honest residue (method extraction, non-public rest fields, nested-target defaults, index-signature rest).
- The ts7 order-parity baseline re-record backfills the entries the released file was missing (2490-2500 included) alongside the new fixtures; no existing entry changed.
2026-07-23 17:27:25 -05:00

70 lines
2.5 KiB
TypeScript

// Destructuring now lowers defaults in every position (tuple, array with
// the bounds test, record fields, nested patterns, whole-pattern and
// rest-pattern parameters, getter results), rest in every declaration
// position (array slices, tuple tails pack fresh, object rest copies the
// unconsumed fields — class instances included, corpus 2539),
// assignment-position patterns over static sources — record AND
// class-instance, with nested patterns, property/element targets, and
// defaults (corpus 2536-2538) — and for-of expression heads. What stays
// fenced is the honest residue: METHOD extraction over class instances
// (a detached method loses its receiver), rest whose packed type is not
// a plain record (a class of methods only packs '{}' — 'unknown'),
// rest that would copy a NON-PUBLIC field (JS copies it; the rest type
// cannot name it), setter-only properties, union sources (narrow
// first), object patterns over arrays, index-signature rest packing,
// defaults on NESTED assignment targets (no single binding type to
// test against), and assignment targets with no static write form.
class C {
f = 1;
describe(): string {
return `f=${this.f}`;
}
set onlyIn(v: number) {
this.f = v;
}
}
const { describe } = new C(); // method extraction keeps the fence
console.log(describe());
const { f, ...restOfC } = new C(); // rest over a class instance keeps the fence
console.log(f, Object.keys(restOfC).length);
const { onlyIn } = new C(); // tsc types the setter-only read; the fence names it
console.log(onlyIn);
interface E1 {
kind: "one";
b: string;
count: number;
}
interface E2 {
kind: "two";
b: string;
label: string;
}
function fromUnion(u: E1 | E2): string {
const { b } = u; // union source: narrow first
return b;
}
console.log(fromUnion({ kind: "one", b: "s", count: 1 }));
const xs = [1, 2, 3];
const { length: len } = xs; // object patterns over arrays keep the fence
console.log(len);
const indexed: Record<string, number> = { a: 1 };
const { ...allOfIt } = indexed; // index-signature rest needs overflow packing
console.log(allOfIt["a"]);
const holder = { inner: { v: 1 } };
let sink = 0;
({ inner: { v: sink } = { v: 2 } } = holder); // a DEFAULT on a nested assignment target keeps the fence
console.log(sink);
class WithPrivate {
pub = 1;
private hidden = 2;
also = 3;
}
const { pub, ...restOfPriv } = new WithPrivate(); // JS copies 'hidden' into the rest object; the rest type cannot name it
console.log(pub, JSON.stringify(restOfPriv));