mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-31 07:23:06 +08:00
- 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.
72 lines
1.8 KiB
TypeScript
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));
|