Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13325,6 +13325,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
const originalBaseType = baseConstructorType.symbol ? getDeclaredTypeOfSymbol(baseConstructorType.symbol) : undefined;
if (
baseConstructorType.symbol && baseConstructorType.symbol.flags & SymbolFlags.Class &&
// Guard against extending a value whose type is a class instance type (rather than the class
// constructor itself). In that case `baseConstructorType` is the instance type, which shares the
// class symbol but whose construct signatures do not return the instance type, so we must resolve
// the base type from those construct signatures below instead of assuming the instance type.
baseConstructorType !== originalBaseType &&
areAllOuterTypeParametersApplied(originalBaseType!)
) {
// When base constructor type is a class with no captured type arguments we know that the constructors all have the same type parameters as the
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//// [tests/cases/compiler/classExtendsConstructorReturningConstructor.ts] ////

=== classExtendsConstructorReturningConstructor.ts ===
// https://github.com/microsoft/TypeScript/issues/63559
// Extending a value whose type is a class instance type carrying a construct
// signature should resolve the base type from that construct signature rather
// than repeating the outer instance type.

export declare const Base: {
>Base : Symbol(Base, Decl(classExtendsConstructorReturningConstructor.ts, 5, 20))

new (): {
kind: "A";
>kind : Symbol(kind, Decl(classExtendsConstructorReturningConstructor.ts, 6, 13))

new (): { kind: "B" };
>kind : Symbol(kind, Decl(classExtendsConstructorReturningConstructor.ts, 8, 17))

};
};

const A = new class extends Base {}();
>A : Symbol(A, Decl(classExtendsConstructorReturningConstructor.ts, 12, 5))
>Base : Symbol(Base, Decl(classExtendsConstructorReturningConstructor.ts, 5, 20))

const B = new class extends A {}();
>B : Symbol(B, Decl(classExtendsConstructorReturningConstructor.ts, 13, 5))
>A : Symbol(A, Decl(classExtendsConstructorReturningConstructor.ts, 12, 5))

const a: "A" = A.kind;
>a : Symbol(a, Decl(classExtendsConstructorReturningConstructor.ts, 15, 5))
>A.kind : Symbol(kind, Decl(classExtendsConstructorReturningConstructor.ts, 6, 13))
>A : Symbol(A, Decl(classExtendsConstructorReturningConstructor.ts, 12, 5))
>kind : Symbol(kind, Decl(classExtendsConstructorReturningConstructor.ts, 6, 13))

const b: "B" = B.kind;
>b : Symbol(b, Decl(classExtendsConstructorReturningConstructor.ts, 16, 5))
>B.kind : Symbol(kind, Decl(classExtendsConstructorReturningConstructor.ts, 8, 17))
>B : Symbol(B, Decl(classExtendsConstructorReturningConstructor.ts, 13, 5))
>kind : Symbol(kind, Decl(classExtendsConstructorReturningConstructor.ts, 8, 17))

Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//// [tests/cases/compiler/classExtendsConstructorReturningConstructor.ts] ////

=== classExtendsConstructorReturningConstructor.ts ===
// https://github.com/microsoft/TypeScript/issues/63559
// Extending a value whose type is a class instance type carrying a construct
// signature should resolve the base type from that construct signature rather
// than repeating the outer instance type.

export declare const Base: {
>Base : new () => { kind: "A"; new (): { kind: "B"; }; }
> : ^^^^^^^^^^

new (): {
kind: "A";
>kind : "A"
> : ^^^

new (): { kind: "B" };
>kind : "B"
> : ^^^

};
};

const A = new class extends Base {}();
>A : (Anonymous class)
> : ^^^^^^^^^^^^^^^^^
>new class extends Base {}() : (Anonymous class)
> : ^^^^^^^^^^^^^^^^^
>class extends Base {} : typeof (Anonymous class)
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>Base : { new (): { kind: "B"; }; kind: "A"; }
> : ^^^^^^^^^^ ^^^^^^^^ ^^^

const B = new class extends A {}();
>B : (Anonymous class)
> : ^^^^^^^^^^^^^^^^^
>new class extends A {}() : (Anonymous class)
> : ^^^^^^^^^^^^^^^^^
>class extends A {} : typeof (Anonymous class)
> : ^^^^^^^^^^^^^^^^^^^^^^^^
>A : { kind: "B"; }
> : ^^^^^^^^ ^^^

const a: "A" = A.kind;
>a : "A"
> : ^^^
>A.kind : "A"
> : ^^^
>A : (Anonymous class)
> : ^^^^^^^^^^^^^^^^^
>kind : "A"
> : ^^^

const b: "B" = B.kind;
>b : "B"
> : ^^^
>B.kind : "B"
> : ^^^
>B : (Anonymous class)
> : ^^^^^^^^^^^^^^^^^
>kind : "B"
> : ^^^

Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// @strict: true
// @noEmit: true

// https://github.com/microsoft/TypeScript/issues/63559
// Extending a value whose type is a class instance type carrying a construct
// signature should resolve the base type from that construct signature rather
// than repeating the outer instance type.

export declare const Base: {
new (): {
kind: "A";
new (): { kind: "B" };
};
};

const A = new class extends Base {}();
const B = new class extends A {}();

const a: "A" = A.kind;
const b: "B" = B.kind;