mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-02 08:35:07 +08:00
- Instantiations carry ts-level bindings alongside the mapped IrTypes: a `K extends keyof T` parameter keys its instance on the bound literal (no cross-literal sharing — the bodies read different fields), and literals forward through generic-to-generic calls. - `T[K]` inside instance bodies resolves through the bound checker types (mapBoundIndexedAccess via the new resolveTypeParamTs hook), and `o[k]` reads whose key TYPE proves one literal — a keyof-bound parameter or a literal-typed const — lower to static field reads. - `const h = id` registers the target's GenericFnInfo under the alias's symbol (chains, block scope, pinned values, and calls all resolve like the target's own name); non-const/reassigned aliases fence by name, and unpinned VALUE uses keep their fence at the reference. - Fixtures 2550 (keyof/pick shapes, forwarding, runtime keys, literal-typed key vars) and 2551 (alias chains, contextual pinning, annotated generic aliases), byte-identical vs Node on both backends.
69 lines
1.9 KiB
TypeScript
69 lines
1.9 KiB
TypeScript
// Mixin edges the compiler rejects (tsc-clean, outside the subset): every
|
|
// fence names what blocks and points at the supported spelling.
|
|
type Ctor<T = object> = new (...args: any[]) => T;
|
|
|
|
class Base {
|
|
n = 1;
|
|
}
|
|
|
|
function M<T extends Ctor<object>>(B: T) {
|
|
return class extends B {
|
|
m = 2;
|
|
};
|
|
}
|
|
|
|
// A mixin call inside a function mints a DISTINCT class per call in JS —
|
|
// one immortal class object cannot be exact there.
|
|
function makeClass() {
|
|
const X = M(Base);
|
|
return new X();
|
|
}
|
|
console.log(makeClass().m);
|
|
|
|
// Builtin classes construct through libCalls, not thunks — they cannot
|
|
// sit under a mixin layer.
|
|
class E extends M(Error) {}
|
|
console.log(new E().m);
|
|
|
|
// Two same-shape instantiations are DISTINCT classes whose subtrees the
|
|
// intersection type cannot tell apart: instance types through the
|
|
// bindings stay unmapped rather than guess.
|
|
const A1 = M(Base);
|
|
const A2 = M(Base);
|
|
const a = new A1();
|
|
const b = new A2();
|
|
console.log(a.m + b.m);
|
|
|
|
// The mixin function itself has no value form: the alias binding
|
|
// registers (calls through it could instantiate per site), but a use
|
|
// with no pinning context fences at the reference.
|
|
const held = M;
|
|
console.log(typeof held);
|
|
|
|
// A rest-parameter mixin constructor compiles only as the pure forwarding
|
|
// shape — anything else reads arguments no static signature carries.
|
|
function Sneaky<T extends Ctor<object>>(B: T) {
|
|
class C extends B {
|
|
argc: number;
|
|
constructor(...args: any[]) {
|
|
super(...args);
|
|
this.argc = args.length;
|
|
}
|
|
}
|
|
return C;
|
|
}
|
|
class S extends Sneaky(Base) {}
|
|
console.log(new S().argc);
|
|
|
|
// The base parameter's flow must be traceable: a mixin that stores or
|
|
// re-reads its parameter is dynamic base flow (recognition declines, and
|
|
// the call keeps the generic machinery's own fences).
|
|
function Leaky<T extends Ctor<object>>(B: T) {
|
|
const saved = B;
|
|
return class extends saved {
|
|
l = 3;
|
|
};
|
|
}
|
|
class L extends Leaky(Base) {}
|
|
console.log(new L().l);
|