Files
scriptc/tests/corpus/2453-private-accessors.ts
Chris Tate f94067ec7e The #private-members chain: fields, methods, accessors, generators
- Privates ride the ordinary member maps under their unspellable spelled names; a subclass redeclaring an inherited private name is fenced, so base-chain walks ARE lexical resolution and privates never enter vtables — JS's no-dynamic-dispatch semantics by construction.
- Private GENERATOR methods compose the generator machinery with this as param 0 through the gen-spawn wrapper (the vtable problem that keeps public generator methods fenced does not exist for privates); async and generic privates ride their existing static-dispatch machinery.
- Private statics lower as module globals/functions through the declaring class's own name; subclass-named and class-value receivers fence (JS brands the declaring constructor alone).
- '#x in obj' lowers as instanceof-the-declarer: static folds at/below and disjoint, the vtable interval test when the declarer sits below the receiver's static class, and unions of decided class arms collapse to tag tests; unknown/generic/static brands fence with pointed hints.
- util.inspect omits private fields exactly like Node (an all-private class prints 'C {}'); corpus 2450-2457 pin every form differentially on both backends, diagnostics private-members pins the fences.
2026-07-23 02:42:10 -05:00

72 lines
1.8 KiB
TypeScript

// #private accessors: get/set pairs collect as "get:#x"/"set:#x" — brand-checked property syntax over the same direct dispatch as private methods. Reads call the getter, writes the setter, compound assignment and ++/-- run get-then-set (observably: a logging pair records the order), getter-only privates serve read-only surfaces, and a throwing private getter is an ordinary catchable error.
class Gauge {
#level = 10;
#trace: string[] = [];
get #pct(): number {
this.#trace.push("get");
return this.#level;
}
set #pct(v: number) {
this.#trace.push(`set${v}`);
this.#level = v;
}
tick(): number {
this.#pct += 5;
this.#pct++;
this.#pct = this.#pct * 2;
return this.#level;
}
trace(): string {
return this.#trace.join(",");
}
}
const g = new Gauge();
console.log(g.tick());
console.log(g.trace());
// Getter-only: a computed read-only surface over private state.
class Circle {
#r: number;
constructor(r: number) {
this.#r = r;
}
get #area(): number {
return Math.round(this.#r * this.#r * 3.14159);
}
grow(): number {
this.#r += 1;
return this.#area;
}
}
const circ = new Circle(2);
console.log(circ.grow(), circ.grow());
// Accessors and methods compose: the setter validates through a private
// method and throws a real, catchable error.
class Clamp {
#v = 0;
#check(n: number): number {
if (n < 0) throw new RangeError("negative");
return n;
}
get #value(): number {
return this.#v;
}
set #value(n: number) {
this.#v = this.#check(n);
}
put(n: number): string {
try {
this.#value = n;
return `ok:${this.#value}`;
} catch (e) {
if (e instanceof RangeError) return `err:${e.message}`;
return "?";
}
}
}
const cl = new Clamp();
console.log(cl.put(4));
console.log(cl.put(-1));
console.log(cl.put(7));