Files
scriptc/tests/diagnostics/mixins.ts
Chris Tate 0386d8c00f keyof-constrained generics monomorphize per literal key; generic-fn aliases register their target
- 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.
2026-07-23 17:20:03 -05:00

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);