diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0a9740a73e414..3427f5036b814 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17715,8 +17715,9 @@ namespace ts { let result = Ternary.True; const saveErrorInfo = captureErrorCalculationState(); const incompatibleReporter = kind === SignatureKind.Construct ? reportIncompatibleConstructSignatureReturn : reportIncompatibleCallSignatureReturn; - - if (getObjectFlags(source) & ObjectFlags.Instantiated && getObjectFlags(target) & ObjectFlags.Instantiated && source.symbol === target.symbol) { + const sourceObjectFlags = getObjectFlags(source); + const targetObjectFlags = getObjectFlags(target); + if (sourceObjectFlags & ObjectFlags.Instantiated && targetObjectFlags & ObjectFlags.Instantiated && source.symbol === target.symbol) { // We have instantiations of the same anonymous type (which typically will be the type of a // method). Simply do a pairwise comparison of the signatures in the two signature lists instead // of the much more expensive N * M comparison matrix we explore below. We erase type parameters @@ -17736,7 +17737,19 @@ namespace ts { // this regardless of the number of signatures, but the potential costs are prohibitive due // to the quadratic nature of the logic below. const eraseGenerics = relation === comparableRelation || !!compilerOptions.noStrictGenericChecks; - result = signatureRelatedTo(sourceSignatures[0], targetSignatures[0], eraseGenerics, reportErrors, incompatibleReporter(sourceSignatures[0], targetSignatures[0])); + const sourceSignature = first(sourceSignatures); + const targetSignature = first(targetSignatures); + result = signatureRelatedTo(sourceSignature, targetSignature, eraseGenerics, reportErrors, incompatibleReporter(sourceSignature, targetSignature)); + if (!result && reportErrors && kind === SignatureKind.Construct && (sourceObjectFlags & targetObjectFlags)) { + const declaration = targetSignature.declaration || sourceSignature.declaration; + if (declaration && (isConstructorDeclaration(declaration) || isConstructSignatureDeclaration(declaration))) { + const constructSignatureToString = (signature: Signature) => + signatureToString(signature, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrowStyleSignature, kind); + reportError(Diagnostics.Type_0_is_not_assignable_to_type_1, constructSignatureToString(sourceSignature), constructSignatureToString(targetSignature)); + reportError(Diagnostics.Types_of_construct_signature_are_incompatible); + return result; + } + } } else { outer: for (const t of targetSignatures) { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 82f3cb4ce579b..59fe0654d703c 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -1686,6 +1686,10 @@ "category": "Error", "code": 2418 }, + "Types of construct signature are incompatible.": { + "category": "Error", + "code": 2419 + }, "Class '{0}' incorrectly implements interface '{1}'.": { "category": "Error", "code": 2420 diff --git a/tests/baselines/reference/assignmentCompatability44.errors.txt b/tests/baselines/reference/assignmentCompatability44.errors.txt new file mode 100644 index 0000000000000..7fa21174f7da0 --- /dev/null +++ b/tests/baselines/reference/assignmentCompatability44.errors.txt @@ -0,0 +1,16 @@ +tests/cases/compiler/assignmentCompatability44.ts(5,7): error TS2322: Type 'typeof Foo' is not assignable to type 'new () => Foo'. + Types of construct signature are incompatible. + Type 'new (x: number) => Foo' is not assignable to type 'new () => Foo'. + + +==== tests/cases/compiler/assignmentCompatability44.ts (1 errors) ==== + class Foo { + constructor(x: number) {} + } + + const foo: { new(): Foo } = Foo; + ~~~ +!!! error TS2322: Type 'typeof Foo' is not assignable to type 'new () => Foo'. +!!! error TS2322: Types of construct signature are incompatible. +!!! error TS2322: Type 'new (x: number) => Foo' is not assignable to type 'new () => Foo'. + \ No newline at end of file diff --git a/tests/baselines/reference/assignmentCompatability44.js b/tests/baselines/reference/assignmentCompatability44.js new file mode 100644 index 0000000000000..ccdbf0827c353 --- /dev/null +++ b/tests/baselines/reference/assignmentCompatability44.js @@ -0,0 +1,15 @@ +//// [assignmentCompatability44.ts] +class Foo { + constructor(x: number) {} +} + +const foo: { new(): Foo } = Foo; + + +//// [assignmentCompatability44.js] +var Foo = /** @class */ (function () { + function Foo(x) { + } + return Foo; +}()); +var foo = Foo; diff --git a/tests/baselines/reference/assignmentCompatability44.symbols b/tests/baselines/reference/assignmentCompatability44.symbols new file mode 100644 index 0000000000000..182bb1b049011 --- /dev/null +++ b/tests/baselines/reference/assignmentCompatability44.symbols @@ -0,0 +1,13 @@ +=== tests/cases/compiler/assignmentCompatability44.ts === +class Foo { +>Foo : Symbol(Foo, Decl(assignmentCompatability44.ts, 0, 0)) + + constructor(x: number) {} +>x : Symbol(x, Decl(assignmentCompatability44.ts, 1, 16)) +} + +const foo: { new(): Foo } = Foo; +>foo : Symbol(foo, Decl(assignmentCompatability44.ts, 4, 5)) +>Foo : Symbol(Foo, Decl(assignmentCompatability44.ts, 0, 0)) +>Foo : Symbol(Foo, Decl(assignmentCompatability44.ts, 0, 0)) + diff --git a/tests/baselines/reference/assignmentCompatability44.types b/tests/baselines/reference/assignmentCompatability44.types new file mode 100644 index 0000000000000..a62bf37184b96 --- /dev/null +++ b/tests/baselines/reference/assignmentCompatability44.types @@ -0,0 +1,12 @@ +=== tests/cases/compiler/assignmentCompatability44.ts === +class Foo { +>Foo : Foo + + constructor(x: number) {} +>x : number +} + +const foo: { new(): Foo } = Foo; +>foo : new () => Foo +>Foo : typeof Foo + diff --git a/tests/baselines/reference/assignmentCompatability45.errors.txt b/tests/baselines/reference/assignmentCompatability45.errors.txt new file mode 100644 index 0000000000000..d4f1e219a75b4 --- /dev/null +++ b/tests/baselines/reference/assignmentCompatability45.errors.txt @@ -0,0 +1,18 @@ +tests/cases/compiler/assignmentCompatability45.ts(7,7): error TS2322: Type 'typeof B' is not assignable to type 'typeof A'. + Types of construct signature are incompatible. + Type 'new (x: number) => B' is not assignable to type 'new () => A'. + + +==== tests/cases/compiler/assignmentCompatability45.ts (1 errors) ==== + abstract class A {} + class B extends A { + constructor(x: number) { + super(); + } + } + const b: typeof A = B; + ~ +!!! error TS2322: Type 'typeof B' is not assignable to type 'typeof A'. +!!! error TS2322: Types of construct signature are incompatible. +!!! error TS2322: Type 'new (x: number) => B' is not assignable to type 'new () => A'. + \ No newline at end of file diff --git a/tests/baselines/reference/assignmentCompatability45.js b/tests/baselines/reference/assignmentCompatability45.js new file mode 100644 index 0000000000000..559714d1f40e5 --- /dev/null +++ b/tests/baselines/reference/assignmentCompatability45.js @@ -0,0 +1,37 @@ +//// [assignmentCompatability45.ts] +abstract class A {} +class B extends A { + constructor(x: number) { + super(); + } +} +const b: typeof A = B; + + +//// [assignmentCompatability45.js] +var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; +})(); +var A = /** @class */ (function () { + function A() { + } + return A; +}()); +var B = /** @class */ (function (_super) { + __extends(B, _super); + function B(x) { + return _super.call(this) || this; + } + return B; +}(A)); +var b = B; diff --git a/tests/baselines/reference/assignmentCompatability45.symbols b/tests/baselines/reference/assignmentCompatability45.symbols new file mode 100644 index 0000000000000..009e89cd044e2 --- /dev/null +++ b/tests/baselines/reference/assignmentCompatability45.symbols @@ -0,0 +1,20 @@ +=== tests/cases/compiler/assignmentCompatability45.ts === +abstract class A {} +>A : Symbol(A, Decl(assignmentCompatability45.ts, 0, 0)) + +class B extends A { +>B : Symbol(B, Decl(assignmentCompatability45.ts, 0, 19)) +>A : Symbol(A, Decl(assignmentCompatability45.ts, 0, 0)) + + constructor(x: number) { +>x : Symbol(x, Decl(assignmentCompatability45.ts, 2, 16)) + + super(); +>super : Symbol(A, Decl(assignmentCompatability45.ts, 0, 0)) + } +} +const b: typeof A = B; +>b : Symbol(b, Decl(assignmentCompatability45.ts, 6, 5)) +>A : Symbol(A, Decl(assignmentCompatability45.ts, 0, 0)) +>B : Symbol(B, Decl(assignmentCompatability45.ts, 0, 19)) + diff --git a/tests/baselines/reference/assignmentCompatability45.types b/tests/baselines/reference/assignmentCompatability45.types new file mode 100644 index 0000000000000..c095da8e8605c --- /dev/null +++ b/tests/baselines/reference/assignmentCompatability45.types @@ -0,0 +1,21 @@ +=== tests/cases/compiler/assignmentCompatability45.ts === +abstract class A {} +>A : A + +class B extends A { +>B : B +>A : A + + constructor(x: number) { +>x : number + + super(); +>super() : void +>super : typeof A + } +} +const b: typeof A = B; +>b : typeof A +>A : typeof A +>B : typeof B + diff --git a/tests/baselines/reference/classSideInheritance3.errors.txt b/tests/baselines/reference/classSideInheritance3.errors.txt index c845defa5b89c..563093c4dec41 100644 --- a/tests/baselines/reference/classSideInheritance3.errors.txt +++ b/tests/baselines/reference/classSideInheritance3.errors.txt @@ -1,4 +1,6 @@ tests/cases/compiler/classSideInheritance3.ts(16,5): error TS2322: Type 'typeof B' is not assignable to type 'typeof A'. + Types of construct signature are incompatible. + Type 'new (x: string, data: string) => B' is not assignable to type 'new (x: string) => A'. tests/cases/compiler/classSideInheritance3.ts(17,5): error TS2322: Type 'typeof B' is not assignable to type 'new (x: string) => A'. @@ -21,6 +23,8 @@ tests/cases/compiler/classSideInheritance3.ts(17,5): error TS2322: Type 'typeof var r1: typeof A = B; // error ~~ !!! error TS2322: Type 'typeof B' is not assignable to type 'typeof A'. +!!! error TS2322: Types of construct signature are incompatible. +!!! error TS2322: Type 'new (x: string, data: string) => B' is not assignable to type 'new (x: string) => A'. var r2: new (x: string) => A = B; // error ~~ !!! error TS2322: Type 'typeof B' is not assignable to type 'new (x: string) => A'. diff --git a/tests/cases/compiler/assignmentCompatability44.ts b/tests/cases/compiler/assignmentCompatability44.ts new file mode 100644 index 0000000000000..c92ad9a4d5029 --- /dev/null +++ b/tests/cases/compiler/assignmentCompatability44.ts @@ -0,0 +1,5 @@ +class Foo { + constructor(x: number) {} +} + +const foo: { new(): Foo } = Foo; diff --git a/tests/cases/compiler/assignmentCompatability45.ts b/tests/cases/compiler/assignmentCompatability45.ts new file mode 100644 index 0000000000000..177a3188c669f --- /dev/null +++ b/tests/cases/compiler/assignmentCompatability45.ts @@ -0,0 +1,7 @@ +abstract class A {} +class B extends A { + constructor(x: number) { + super(); + } +} +const b: typeof A = B;