Files
scriptc/tests/corpus/2452-private-statics.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

57 lines
1.7 KiB
TypeScript

// #private STATICS: `static #field` is a module global assigned at the class statement's position (static blocks included — they run in member order, writing through the declaring class's own name), `static #method` is an ordinary module function `%C.static:#m`, and generic private statics monomorphize per call site. Access always spells the declaring class's name — exactly the surface tsc admits for privates.
class Registry {
static #serial = 0;
static #prefix = "id-";
static #next(): number {
Registry.#serial = Registry.#serial + 1;
return Registry.#serial;
}
static {
Registry.#serial = 10;
}
static issue(): string {
return Registry.#prefix + Registry.#next();
}
static #brand<T>(v: T): string {
return `${Registry.#prefix}${typeof v}`;
}
static brands(): string {
return Registry.#brand(1) + "/" + Registry.#brand("s");
}
}
console.log(Registry.issue());
console.log(Registry.issue());
console.log(Registry.brands());
// Instance methods reach private statics through the class name.
class Counter {
static #count = 0;
bump(): number {
Counter.#count = Counter.#count + 1;
return Counter.#count;
}
static read(): number {
return Counter.#count;
}
}
const c1 = new Counter();
const c2 = new Counter();
console.log(c1.bump(), c2.bump(), c1.bump());
console.log(Counter.read());
// Async private statics ride the async-static machinery.
class Loader {
static #stamp = "v1";
static async #fetch(key: string): Promise<string> {
return `${Loader.#stamp}:${key}`;
}
static async load(key: string): Promise<string> {
const raw = await Loader.#fetch(key);
return raw.toUpperCase();
}
}
async function main(): Promise<void> {
console.log(await Loader.load("cfg"));
}
void main();