mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-28 06:53:42 +08:00
Extend a generic factory's anonymous local class returned through a constructor type
A generic factory declared to return a constructor type now also extends
when it returns an anonymous class expression (`return class { ... } as
Struct<S>`) or a `const` bound to one (`const Hold = class { ... }; return
Hold`). The result's class value is the class expression's own
constructor type; before, it was read through the class's name, so an
anonymous class refused with SC1090 and SC2008.
This commit is contained in:
@@ -496,10 +496,18 @@ function localClassResultInstance(
|
||||
const viaConstructorType = retTs.getSymbol()?.valueDeclaration !== resultClass;
|
||||
const returnType = withInstanceScope(lowerer, info, inst, () => {
|
||||
if (!viaConstructorType) return lowerer.mapTypeOf(retTs);
|
||||
const classSymbol = resultClass.name
|
||||
? lowerer.checker.getSymbolAtLocation(resultClass.name)
|
||||
: undefined;
|
||||
return classSymbol ? lowerer.mapTypeOf(lowerer.checker.getTypeOfSymbol(classSymbol)) : null;
|
||||
// The local class's own constructor type: a class expression's type is
|
||||
// its constructor type, named or not (`return class { ... } as Struct<S>`).
|
||||
const classSymbol =
|
||||
!ts.isClassExpression(resultClass) && resultClass.name
|
||||
? lowerer.checker.getSymbolAtLocation(resultClass.name)
|
||||
: undefined;
|
||||
const classType = ts.isClassExpression(resultClass)
|
||||
? lowerer.checker.getTypeAtLocation(resultClass)
|
||||
: classSymbol
|
||||
? lowerer.checker.getTypeOfSymbol(classSymbol)
|
||||
: undefined;
|
||||
return classType ? lowerer.mapTypeOf(classType) : null;
|
||||
});
|
||||
if (!returnType) {
|
||||
fenceGenericSignatureResult(lowerer, expr, retTs);
|
||||
@@ -646,11 +654,19 @@ function returnedLocalClass(lowerer: Lowerer, info: GenericFnInfo): ts.ClassLike
|
||||
if (ts.isClassExpression(expr)) return declaredInside(expr, info.decl) ? expr : null;
|
||||
if (!ts.isIdentifier(expr)) return null;
|
||||
const declaration = lowerer.checker.getSymbolAtLocation(expr)?.valueDeclaration;
|
||||
return declaration &&
|
||||
ts.isClassDeclaration(declaration) &&
|
||||
declaredInside(declaration, info.decl)
|
||||
? declaration
|
||||
: null;
|
||||
if (!declaration || !declaredInside(declaration, info.decl)) return null;
|
||||
if (ts.isClassDeclaration(declaration)) return declaration;
|
||||
// A `const` bound to a class expression (`const C = class { ... }; return C`).
|
||||
if (
|
||||
ts.isVariableDeclaration(declaration) &&
|
||||
ts.getCombinedNodeFlags(declaration) & ts.NodeFlags.Const &&
|
||||
declaration.initializer
|
||||
) {
|
||||
let init: ts.Expression = declaration.initializer;
|
||||
while (ts.isParenthesizedExpression(init)) init = init.expression;
|
||||
if (ts.isClassExpression(init)) return init;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
if (!ts.isBlock(body)) return classOf(body as ts.Expression);
|
||||
let found: ts.ClassLikeDeclaration | null = null;
|
||||
|
||||
@@ -65,3 +65,35 @@ function twice(n: number): number {
|
||||
return K.double() + K.quad();
|
||||
}
|
||||
console.log(twice(3), twice(5));
|
||||
|
||||
// The factory returns an anonymous class expression, directly or through a
|
||||
// `const` binding.
|
||||
function anonymous<S>(value: S): Struct<S> {
|
||||
return class {
|
||||
static decode(_input: unknown): S {
|
||||
return value;
|
||||
}
|
||||
} as unknown as Struct<S>;
|
||||
}
|
||||
class Anonymous extends anonymous({ tag: "anon" }) {}
|
||||
console.log(Anonymous.decode(null).tag);
|
||||
|
||||
type Holder<T> = (new (held: T) => { held: T }) & { readonly seed: () => T };
|
||||
function holder<T>(initial: T): Holder<T> {
|
||||
const Hold = class {
|
||||
held: T;
|
||||
constructor(held: T) {
|
||||
this.held = held;
|
||||
}
|
||||
static seed(): T {
|
||||
return initial;
|
||||
}
|
||||
};
|
||||
return Hold;
|
||||
}
|
||||
class Boxed extends holder(7) {
|
||||
twice(): number {
|
||||
return this.held * 2;
|
||||
}
|
||||
}
|
||||
console.log(new Boxed(4).twice(), Boxed.seed());
|
||||
|
||||
Reference in New Issue
Block a user