Skip to content

fix(31046): more accurate error message when derived class has different constructor signature #40073

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 22, 2020
Merged
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
17 changes: 14 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -17736,7 +17737,17 @@ 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) &&
(targetSignature.declaration?.kind === SyntaxKind.Constructor || sourceSignature.declaration?.kind === SyntaxKind.Constructor)) {
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_signatures_are_incompatible);
return result;
}
}
else {
outer: for (const t of targetSignatures) {
Expand Down
4 changes: 4 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1686,6 +1686,10 @@
"category": "Error",
"code": 2418
},
"Types of construct signatures are incompatible.": {
"category": "Error",
"code": 2419
},
"Class '{0}' incorrectly implements interface '{1}'.": {
"category": "Error",
"code": 2420
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@ tests/cases/compiler/assignmentCompatWithOverloads.ts(19,1): error TS2322: Type
tests/cases/compiler/assignmentCompatWithOverloads.ts(21,1): error TS2322: Type '{ (x: string): string; (x: number): number; }' is not assignable to type '(s1: string) => number'.
Type 'string' is not assignable to type 'number'.
tests/cases/compiler/assignmentCompatWithOverloads.ts(30,1): error TS2322: Type 'typeof C' is not assignable to type 'new (x: number) => void'.
Types of parameters 'x' and 'x' are incompatible.
Type 'number' is not assignable to type 'string'.
Types of construct signatures are incompatible.
Type 'new (x: string) => C' is not assignable to type 'new (x: number) => void'.
Types of parameters 'x' and 'x' are incompatible.
Type 'number' is not assignable to type 'string'.


==== tests/cases/compiler/assignmentCompatWithOverloads.ts (4 errors) ====
Expand Down Expand Up @@ -53,5 +55,7 @@ tests/cases/compiler/assignmentCompatWithOverloads.ts(30,1): error TS2322: Type
d = C; // Error
~
!!! error TS2322: Type 'typeof C' is not assignable to type 'new (x: number) => void'.
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
!!! error TS2322: Type 'number' is not assignable to type 'string'.
!!! error TS2322: Types of construct signatures are incompatible.
!!! error TS2322: Type 'new (x: string) => C' is not assignable to type 'new (x: number) => void'.
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
!!! error TS2322: Type 'number' is not assignable to type 'string'.
16 changes: 16 additions & 0 deletions tests/baselines/reference/assignmentCompatability44.errors.txt
Original file line number Diff line number Diff line change
@@ -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 signatures 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 signatures are incompatible.
!!! error TS2322: Type 'new (x: number) => Foo' is not assignable to type 'new () => Foo'.

15 changes: 15 additions & 0 deletions tests/baselines/reference/assignmentCompatability44.js
Original file line number Diff line number Diff line change
@@ -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;
13 changes: 13 additions & 0 deletions tests/baselines/reference/assignmentCompatability44.symbols
Original file line number Diff line number Diff line change
@@ -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))

12 changes: 12 additions & 0 deletions tests/baselines/reference/assignmentCompatability44.types
Original file line number Diff line number Diff line change
@@ -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

18 changes: 18 additions & 0 deletions tests/baselines/reference/assignmentCompatability45.errors.txt
Original file line number Diff line number Diff line change
@@ -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 signatures 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 signatures are incompatible.
!!! error TS2322: Type 'new (x: number) => B' is not assignable to type 'new () => A'.

37 changes: 37 additions & 0 deletions tests/baselines/reference/assignmentCompatability45.js
Original file line number Diff line number Diff line change
@@ -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;
20 changes: 20 additions & 0 deletions tests/baselines/reference/assignmentCompatability45.symbols
Original file line number Diff line number Diff line change
@@ -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))

21 changes: 21 additions & 0 deletions tests/baselines/reference/assignmentCompatability45.types
Original file line number Diff line number Diff line change
@@ -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

8 changes: 8 additions & 0 deletions tests/baselines/reference/classSideInheritance3.errors.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
tests/cases/compiler/classSideInheritance3.ts(16,5): error TS2322: Type 'typeof B' is not assignable to type 'typeof A'.
Types of construct signatures 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'.
Types of construct signatures are incompatible.
Type 'new (x: string, data: string) => B' is not assignable to type 'new (x: string) => A'.


==== tests/cases/compiler/classSideInheritance3.ts (2 errors) ====
Expand All @@ -21,7 +25,11 @@ 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 signatures 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'.
!!! error TS2322: Types of construct signatures are incompatible.
!!! error TS2322: Type 'new (x: string, data: string) => B' is not assignable to type 'new (x: string) => A'.
var r3: typeof A = C; // ok
16 changes: 10 additions & 6 deletions tests/baselines/reference/strictBindCallApply1.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,11 @@ tests/cases/conformance/functions/strictBindCallApply1.ts(62,11): error TS2769:
Argument of type 'number' is not assignable to parameter of type 'string'.
Overload 2 of 6, '(this: new (...args: (10 | 20)[]) => C, thisArg: any, ...args: (10 | 20)[]): new (...args: (10 | 20)[]) => C', gave the following error.
The 'this' context of type 'typeof C' is not assignable to method's 'this' of type 'new (...args: (10 | 20)[]) => C'.
Types of parameters 'b' and 'args' are incompatible.
Type 'number' is not assignable to type 'string'.
Type 'number' is not assignable to type 'string'.
Types of construct signatures are incompatible.
Type 'new (a: number, b: string) => C' is not assignable to type 'new (...args: (10 | 20)[]) => C'.
Types of parameters 'b' and 'args' are incompatible.
Type 'number' is not assignable to type 'string'.
Type 'number' is not assignable to type 'string'.
tests/cases/conformance/functions/strictBindCallApply1.ts(65,3): error TS2554: Expected 3 arguments, but got 2.
tests/cases/conformance/functions/strictBindCallApply1.ts(66,15): error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
tests/cases/conformance/functions/strictBindCallApply1.ts(67,24): error TS2554: Expected 3 arguments, but got 4.
Expand Down Expand Up @@ -186,9 +188,11 @@ tests/cases/conformance/functions/strictBindCallApply1.ts(72,12): error TS2345:
!!! error TS2769: Argument of type 'number' is not assignable to parameter of type 'string'.
!!! error TS2769: Overload 2 of 6, '(this: new (...args: (10 | 20)[]) => C, thisArg: any, ...args: (10 | 20)[]): new (...args: (10 | 20)[]) => C', gave the following error.
!!! error TS2769: The 'this' context of type 'typeof C' is not assignable to method's 'this' of type 'new (...args: (10 | 20)[]) => C'.
!!! error TS2769: Types of parameters 'b' and 'args' are incompatible.
!!! error TS2769: Type 'number' is not assignable to type 'string'.
!!! error TS2769: Type 'number' is not assignable to type 'string'.
!!! error TS2769: Types of construct signatures are incompatible.
!!! error TS2769: Type 'new (a: number, b: string) => C' is not assignable to type 'new (...args: (10 | 20)[]) => C'.
!!! error TS2769: Types of parameters 'b' and 'args' are incompatible.
!!! error TS2769: Type 'number' is not assignable to type 'string'.
!!! error TS2769: Type 'number' is not assignable to type 'string'.

C.call(c, 10, "hello");
C.call(c, 10); // Error
Expand Down
5 changes: 5 additions & 0 deletions tests/cases/compiler/assignmentCompatability44.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class Foo {
constructor(x: number) {}
}

const foo: { new(): Foo } = Foo;
7 changes: 7 additions & 0 deletions tests/cases/compiler/assignmentCompatability45.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
abstract class A {}
class B extends A {
constructor(x: number) {
super();
}
}
const b: typeof A = B;