Skip to content

Commit 22d94fd

Browse files
committed
fix(31046): add new diagnostic message for incompatible constructor signature
1 parent 610fa28 commit 22d94fd

13 files changed

+188
-3
lines changed

src/compiler/checker.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17715,8 +17715,9 @@ namespace ts {
1771517715
let result = Ternary.True;
1771617716
const saveErrorInfo = captureErrorCalculationState();
1771717717
const incompatibleReporter = kind === SignatureKind.Construct ? reportIncompatibleConstructSignatureReturn : reportIncompatibleCallSignatureReturn;
17718-
17719-
if (getObjectFlags(source) & ObjectFlags.Instantiated && getObjectFlags(target) & ObjectFlags.Instantiated && source.symbol === target.symbol) {
17718+
const sourceObjectFlags = getObjectFlags(source);
17719+
const targetObjectFlags = getObjectFlags(target);
17720+
if (sourceObjectFlags & ObjectFlags.Instantiated && targetObjectFlags & ObjectFlags.Instantiated && source.symbol === target.symbol) {
1772017721
// We have instantiations of the same anonymous type (which typically will be the type of a
1772117722
// method). Simply do a pairwise comparison of the signatures in the two signature lists instead
1772217723
// of the much more expensive N * M comparison matrix we explore below. We erase type parameters
@@ -17736,7 +17737,19 @@ namespace ts {
1773617737
// this regardless of the number of signatures, but the potential costs are prohibitive due
1773717738
// to the quadratic nature of the logic below.
1773817739
const eraseGenerics = relation === comparableRelation || !!compilerOptions.noStrictGenericChecks;
17739-
result = signatureRelatedTo(sourceSignatures[0], targetSignatures[0], eraseGenerics, reportErrors, incompatibleReporter(sourceSignatures[0], targetSignatures[0]));
17740+
const sourceSignature = first(sourceSignatures);
17741+
const targetSignature = first(targetSignatures);
17742+
result = signatureRelatedTo(sourceSignature, targetSignature, eraseGenerics, reportErrors, incompatibleReporter(sourceSignature, targetSignature));
17743+
if (!result && reportErrors && kind === SignatureKind.Construct && (sourceObjectFlags & targetObjectFlags)) {
17744+
const declaration = targetSignature.declaration || sourceSignature.declaration;
17745+
if (declaration && (isConstructorDeclaration(declaration) || isConstructSignatureDeclaration(declaration))) {
17746+
const constructSignatureToString = (signature: Signature) =>
17747+
signatureToString(signature, /*enclosingDeclaration*/ undefined, TypeFormatFlags.WriteArrowStyleSignature, kind);
17748+
reportError(Diagnostics.Type_0_is_not_assignable_to_type_1, constructSignatureToString(sourceSignature), constructSignatureToString(targetSignature));
17749+
reportError(Diagnostics.Types_of_construct_signature_are_incompatible);
17750+
return result;
17751+
}
17752+
}
1774017753
}
1774117754
else {
1774217755
outer: for (const t of targetSignatures) {

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1686,6 +1686,10 @@
16861686
"category": "Error",
16871687
"code": 2418
16881688
},
1689+
"Types of construct signature are incompatible.": {
1690+
"category": "Error",
1691+
"code": 2419
1692+
},
16891693
"Class '{0}' incorrectly implements interface '{1}'.": {
16901694
"category": "Error",
16911695
"code": 2420
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
tests/cases/compiler/assignmentCompatability44.ts(5,7): error TS2322: Type 'typeof Foo' is not assignable to type 'new () => Foo'.
2+
Types of construct signature are incompatible.
3+
Type 'new (x: number) => Foo' is not assignable to type 'new () => Foo'.
4+
5+
6+
==== tests/cases/compiler/assignmentCompatability44.ts (1 errors) ====
7+
class Foo {
8+
constructor(x: number) {}
9+
}
10+
11+
const foo: { new(): Foo } = Foo;
12+
~~~
13+
!!! error TS2322: Type 'typeof Foo' is not assignable to type 'new () => Foo'.
14+
!!! error TS2322: Types of construct signature are incompatible.
15+
!!! error TS2322: Type 'new (x: number) => Foo' is not assignable to type 'new () => Foo'.
16+
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//// [assignmentCompatability44.ts]
2+
class Foo {
3+
constructor(x: number) {}
4+
}
5+
6+
const foo: { new(): Foo } = Foo;
7+
8+
9+
//// [assignmentCompatability44.js]
10+
var Foo = /** @class */ (function () {
11+
function Foo(x) {
12+
}
13+
return Foo;
14+
}());
15+
var foo = Foo;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
=== tests/cases/compiler/assignmentCompatability44.ts ===
2+
class Foo {
3+
>Foo : Symbol(Foo, Decl(assignmentCompatability44.ts, 0, 0))
4+
5+
constructor(x: number) {}
6+
>x : Symbol(x, Decl(assignmentCompatability44.ts, 1, 16))
7+
}
8+
9+
const foo: { new(): Foo } = Foo;
10+
>foo : Symbol(foo, Decl(assignmentCompatability44.ts, 4, 5))
11+
>Foo : Symbol(Foo, Decl(assignmentCompatability44.ts, 0, 0))
12+
>Foo : Symbol(Foo, Decl(assignmentCompatability44.ts, 0, 0))
13+
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
=== tests/cases/compiler/assignmentCompatability44.ts ===
2+
class Foo {
3+
>Foo : Foo
4+
5+
constructor(x: number) {}
6+
>x : number
7+
}
8+
9+
const foo: { new(): Foo } = Foo;
10+
>foo : new () => Foo
11+
>Foo : typeof Foo
12+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
tests/cases/compiler/assignmentCompatability45.ts(7,7): error TS2322: Type 'typeof B' is not assignable to type 'typeof A'.
2+
Types of construct signature are incompatible.
3+
Type 'new (x: number) => B' is not assignable to type 'new () => A'.
4+
5+
6+
==== tests/cases/compiler/assignmentCompatability45.ts (1 errors) ====
7+
abstract class A {}
8+
class B extends A {
9+
constructor(x: number) {
10+
super();
11+
}
12+
}
13+
const b: typeof A = B;
14+
~
15+
!!! error TS2322: Type 'typeof B' is not assignable to type 'typeof A'.
16+
!!! error TS2322: Types of construct signature are incompatible.
17+
!!! error TS2322: Type 'new (x: number) => B' is not assignable to type 'new () => A'.
18+
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//// [assignmentCompatability45.ts]
2+
abstract class A {}
3+
class B extends A {
4+
constructor(x: number) {
5+
super();
6+
}
7+
}
8+
const b: typeof A = B;
9+
10+
11+
//// [assignmentCompatability45.js]
12+
var __extends = (this && this.__extends) || (function () {
13+
var extendStatics = function (d, b) {
14+
extendStatics = Object.setPrototypeOf ||
15+
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
16+
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
17+
return extendStatics(d, b);
18+
};
19+
return function (d, b) {
20+
extendStatics(d, b);
21+
function __() { this.constructor = d; }
22+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
23+
};
24+
})();
25+
var A = /** @class */ (function () {
26+
function A() {
27+
}
28+
return A;
29+
}());
30+
var B = /** @class */ (function (_super) {
31+
__extends(B, _super);
32+
function B(x) {
33+
return _super.call(this) || this;
34+
}
35+
return B;
36+
}(A));
37+
var b = B;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
=== tests/cases/compiler/assignmentCompatability45.ts ===
2+
abstract class A {}
3+
>A : Symbol(A, Decl(assignmentCompatability45.ts, 0, 0))
4+
5+
class B extends A {
6+
>B : Symbol(B, Decl(assignmentCompatability45.ts, 0, 19))
7+
>A : Symbol(A, Decl(assignmentCompatability45.ts, 0, 0))
8+
9+
constructor(x: number) {
10+
>x : Symbol(x, Decl(assignmentCompatability45.ts, 2, 16))
11+
12+
super();
13+
>super : Symbol(A, Decl(assignmentCompatability45.ts, 0, 0))
14+
}
15+
}
16+
const b: typeof A = B;
17+
>b : Symbol(b, Decl(assignmentCompatability45.ts, 6, 5))
18+
>A : Symbol(A, Decl(assignmentCompatability45.ts, 0, 0))
19+
>B : Symbol(B, Decl(assignmentCompatability45.ts, 0, 19))
20+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
=== tests/cases/compiler/assignmentCompatability45.ts ===
2+
abstract class A {}
3+
>A : A
4+
5+
class B extends A {
6+
>B : B
7+
>A : A
8+
9+
constructor(x: number) {
10+
>x : number
11+
12+
super();
13+
>super() : void
14+
>super : typeof A
15+
}
16+
}
17+
const b: typeof A = B;
18+
>b : typeof A
19+
>A : typeof A
20+
>B : typeof B
21+

tests/baselines/reference/classSideInheritance3.errors.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
tests/cases/compiler/classSideInheritance3.ts(16,5): error TS2322: Type 'typeof B' is not assignable to type 'typeof A'.
2+
Types of construct signature are incompatible.
3+
Type 'new (x: string, data: string) => B' is not assignable to type 'new (x: string) => A'.
24
tests/cases/compiler/classSideInheritance3.ts(17,5): error TS2322: Type 'typeof B' is not assignable to type 'new (x: string) => A'.
35

46

@@ -21,6 +23,8 @@ tests/cases/compiler/classSideInheritance3.ts(17,5): error TS2322: Type 'typeof
2123
var r1: typeof A = B; // error
2224
~~
2325
!!! error TS2322: Type 'typeof B' is not assignable to type 'typeof A'.
26+
!!! error TS2322: Types of construct signature are incompatible.
27+
!!! error TS2322: Type 'new (x: string, data: string) => B' is not assignable to type 'new (x: string) => A'.
2428
var r2: new (x: string) => A = B; // error
2529
~~
2630
!!! error TS2322: Type 'typeof B' is not assignable to type 'new (x: string) => A'.
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
class Foo {
2+
constructor(x: number) {}
3+
}
4+
5+
const foo: { new(): Foo } = Foo;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
abstract class A {}
2+
class B extends A {
3+
constructor(x: number) {
4+
super();
5+
}
6+
}
7+
const b: typeof A = B;

0 commit comments

Comments
 (0)