// #private-member FENCES: the shapes the private-members lowering refuses, // each with the diagnostic naming the honest reason. Everything here is // legal TypeScript that Node runs — the fences are representation limits, // not type errors. // A subclass redeclaring an inherited private NAME: JS gives each class its // own distinct '#x' slot under one spelling; these layouts have one slot // per name. class RedeclareBase { #x = 1; read(): number { return this.#x; } } class RedeclareSub extends RedeclareBase { #x = 2; own(): number { return this.#x; } } const rs = new RedeclareSub(); console.log(rs.read()); // Extracted private methods are NOT auto-bound in JS (`f()` runs with // `this` undefined) — the unbound value has no static story, the // bound-method fence names it. class Unbound { #m(): number { return 1; } grab(): () => number { const f = this.#m; return f; } } console.log(new Unbound().grab()()); // Brand checks need a receiver whose representation can HOLD a branded // instance: 'unknown' values live in the checked-dynamic tree, which never carries class // instances — the fence points at narrowing. class BrandUnknown { #v = 3; static probe(o: unknown): boolean { return typeof o === "object" && o !== null && #v in o; } } console.log(BrandUnknown.probe(null)); // A private STATIC's brand lives on the declaring class OBJECT, not on // instances — the instance-shaped test fences. class StaticBrand { static #s(): number { return 1; } check(o: StaticBrand): boolean { return #s in o; } } console.log(new StaticBrand().check(new StaticBrand())); // Private statics through class VALUES that may hold a DESCENDANT: with a // subclass in the program, the slot can hold it at runtime and JS brands // the declaring class object alone (exact receivers — the name, a const // alias, a leaf class's values — compile). class ViaValue { static #origin = 7; static read(k: typeof ViaValue): number { return k.#origin; } } class ViaValueSub extends ViaValue {} console.log(ViaValue.read(ViaValueSub)); // Generic classes share ONE brand across every instantiation in JS; these // layouts mint one class per instantiation, so private-in fences there. class GenBrand { #tag = 1; same(o: GenBrand): boolean { return #tag in o; } } console.log(new GenBrand().same(new GenBrand())); // Positive control: async private generators use direct dispatch and the // same request queue as module-level async generators. class AsyncGen { async *#pump(): AsyncGenerator { yield 1; } async drain(): Promise { for await (const v of this.#pump()) console.log(v); } } const ag = new AsyncGen(); void ag.drain();