mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-24 06:14:01 +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.
57 lines
1.7 KiB
TypeScript
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();
|