From 2dd7a4bf939ffac4002ea9c76651166ba457ac70 Mon Sep 17 00:00:00 2001 From: Alexander T Date: Sat, 22 Aug 2020 23:26:13 +0300 Subject: [PATCH] fix(31046): add new diagnostic message for incompatible constructor signature (#40073) --- src/compiler/checker.ts | 17 +++++++-- src/compiler/diagnosticMessages.json | 4 ++ .../assignmentCompatWithOverloads.errors.txt | 12 ++++-- .../assignmentCompatability44.errors.txt | 16 ++++++++ .../reference/assignmentCompatability44.js | 15 ++++++++ .../assignmentCompatability44.symbols | 13 +++++++ .../reference/assignmentCompatability44.types | 12 ++++++ .../assignmentCompatability45.errors.txt | 18 +++++++++ .../reference/assignmentCompatability45.js | 37 +++++++++++++++++++ .../assignmentCompatability45.symbols | 20 ++++++++++ .../reference/assignmentCompatability45.types | 21 +++++++++++ .../classSideInheritance3.errors.txt | 8 ++++ .../reference/strictBindCallApply1.errors.txt | 16 +++++--- .../compiler/assignmentCompatability44.ts | 5 +++ .../compiler/assignmentCompatability45.ts | 7 ++++ 15 files changed, 208 insertions(+), 13 deletions(-) create mode 100644 tests/baselines/reference/assignmentCompatability44.errors.txt create mode 100644 tests/baselines/reference/assignmentCompatability44.js create mode 100644 tests/baselines/reference/assignmentCompatability44.symbols create mode 100644 tests/baselines/reference/assignmentCompatability44.types create mode 100644 tests/baselines/reference/assignmentCompatability45.errors.txt create mode 100644 tests/baselines/reference/assignmentCompatability45.js create mode 100644 tests/baselines/reference/assignmentCompatability45.symbols create mode 100644 tests/baselines/reference/assignmentCompatability45.types create mode 100644 tests/cases/compiler/assignmentCompatability44.ts create mode 100644 tests/cases/compiler/assignmentCompatability45.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index a85281375e6b9..9d9364a12f6d7 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -17772,8 +17772,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 @@ -17793,7 +17794,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) { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index a5a7c2b3d3d12..e50c7e35a95fa 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -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 diff --git a/tests/baselines/reference/assignmentCompatWithOverloads.errors.txt b/tests/baselines/reference/assignmentCompatWithOverloads.errors.txt index 3773229f5161f..24f06462b8af4 100644 --- a/tests/baselines/reference/assignmentCompatWithOverloads.errors.txt +++ b/tests/baselines/reference/assignmentCompatWithOverloads.errors.txt @@ -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) ==== @@ -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'. \ No newline at end of file +!!! 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'. \ No newline at end of file diff --git a/tests/baselines/reference/assignmentCompatability44.errors.txt b/tests/baselines/reference/assignmentCompatability44.errors.txt new file mode 100644 index 0000000000000..65cd1826acc23 --- /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 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'. + \ 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..7a01b39841be6 --- /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 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'. + \ 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..cdad659971592 100644 --- a/tests/baselines/reference/classSideInheritance3.errors.txt +++ b/tests/baselines/reference/classSideInheritance3.errors.txt @@ -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) ==== @@ -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 \ No newline at end of file diff --git a/tests/baselines/reference/strictBindCallApply1.errors.txt b/tests/baselines/reference/strictBindCallApply1.errors.txt index c9e62c74f0c47..83576247bc096 100644 --- a/tests/baselines/reference/strictBindCallApply1.errors.txt +++ b/tests/baselines/reference/strictBindCallApply1.errors.txt @@ -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. @@ -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 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;