Skip to content

Commit 7664f34

Browse files
author
Arthur Ozga
committed
Merge pull request microsoft#3722 from Microsoft/abstractConstructorAssignability
Abstract constructor assignability
2 parents 2a9ab73 + 50f254b commit 7664f34

25 files changed

+602
-10
lines changed

src/compiler/checker.ts

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4903,9 +4903,38 @@ namespace ts {
49034903
let targetSignatures = getSignaturesOfType(target, kind);
49044904
let result = Ternary.True;
49054905
let saveErrorInfo = errorInfo;
4906+
4907+
// Because the "abstractness" of a class is the same across all construct signatures
4908+
// (internally we are checking the corresponding declaration), it is enough to perform
4909+
// the check and report an error once over all pairs of source and target construct signatures.
4910+
let sourceSig = sourceSignatures[0];
4911+
// Note that in an extends-clause, targetSignatures is stripped, so the check never proceeds.
4912+
let targetSig = targetSignatures[0];
4913+
4914+
if (sourceSig && targetSig) {
4915+
let sourceErasedSignature = getErasedSignature(sourceSig);
4916+
let targetErasedSignature = getErasedSignature(targetSig);
4917+
4918+
let sourceReturnType = sourceErasedSignature && getReturnTypeOfSignature(sourceErasedSignature);
4919+
let targetReturnType = targetErasedSignature && getReturnTypeOfSignature(targetErasedSignature);
4920+
4921+
let sourceReturnDecl = sourceReturnType && sourceReturnType.symbol && getDeclarationOfKind(sourceReturnType.symbol, SyntaxKind.ClassDeclaration);
4922+
let targetReturnDecl = targetReturnType && targetReturnType.symbol && getDeclarationOfKind(targetReturnType.symbol, SyntaxKind.ClassDeclaration);
4923+
let sourceIsAbstract = sourceReturnDecl && sourceReturnDecl.flags & NodeFlags.Abstract;
4924+
let targetIsAbstract = targetReturnDecl && targetReturnDecl.flags & NodeFlags.Abstract;
4925+
4926+
if (sourceIsAbstract && !targetIsAbstract) {
4927+
if (reportErrors) {
4928+
reportError(Diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type);
4929+
}
4930+
return Ternary.False;
4931+
}
4932+
}
4933+
49064934
outer: for (let t of targetSignatures) {
49074935
if (!t.hasStringLiterals || target.flags & TypeFlags.FromSignature) {
49084936
let localErrors = reportErrors;
4937+
let checkedAbstractAssignability = false;
49094938
for (let s of sourceSignatures) {
49104939
if (!s.hasStringLiterals || source.flags & TypeFlags.FromSignature) {
49114940
let related = signatureRelatedTo(s, t, localErrors);
@@ -5012,10 +5041,11 @@ namespace ts {
50125041
return Ternary.False;
50135042
}
50145043

5015-
let t = getReturnTypeOfSignature(target);
5016-
if (t === voidType) return result;
5017-
let s = getReturnTypeOfSignature(source);
5018-
return result & isRelatedTo(s, t, reportErrors);
5044+
let targetReturnType = getReturnTypeOfSignature(target);
5045+
if (targetReturnType === voidType) return result;
5046+
let sourceReturnType = getReturnTypeOfSignature(source);
5047+
5048+
return result & isRelatedTo(sourceReturnType, targetReturnType, reportErrors);
50195049
}
50205050

50215051
function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary {

src/compiler/diagnosticInformationMap.generated.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ namespace ts {
407407
Classes_containing_abstract_methods_must_be_marked_abstract: { code: 2514, category: DiagnosticCategory.Error, key: "Classes containing abstract methods must be marked abstract." },
408408
Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2: { code: 2515, category: DiagnosticCategory.Error, key: "Non-abstract class '{0}' does not implement inherited abstract member '{1}' from class '{2}'." },
409409
All_declarations_of_an_abstract_method_must_be_consecutive: { code: 2516, category: DiagnosticCategory.Error, key: "All declarations of an abstract method must be consecutive." },
410-
Constructor_objects_of_abstract_type_cannot_be_assigned_to_constructor_objects_of_non_abstract_type: { code: 2517, category: DiagnosticCategory.Error, key: "Constructor objects of abstract type cannot be assigned to constructor objects of non-abstract type" },
410+
Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type: { code: 2517, category: DiagnosticCategory.Error, key: "Cannot assign an abstract constructor type to a non-abstract constructor type." },
411411
Only_an_ambient_class_can_be_merged_with_an_interface: { code: 2518, category: DiagnosticCategory.Error, key: "Only an ambient class can be merged with an interface." },
412412
Duplicate_identifier_0_Compiler_uses_declaration_1_to_support_async_functions: { code: 2520, category: DiagnosticCategory.Error, key: "Duplicate identifier '{0}'. Compiler uses declaration '{1}' to support async functions." },
413413
Expression_resolves_to_variable_declaration_0_that_compiler_uses_to_support_async_functions: { code: 2521, category: DiagnosticCategory.Error, key: "Expression resolves to variable declaration '{0}' that compiler uses to support async functions." },

src/compiler/diagnosticMessages.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1617,7 +1617,7 @@
16171617
"category": "Error",
16181618
"code": 2516
16191619
},
1620-
"Constructor objects of abstract type cannot be assigned to constructor objects of non-abstract type": {
1620+
"Cannot assign an abstract constructor type to a non-abstract constructor type.": {
16211621
"category": "Error",
16221622
"code":2517
16231623
},
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractClinterfaceAssignability.ts(23,1): error TS2322: Type 'typeof A' is not assignable to type 'IConstructor'.
2+
Cannot assign an abstract constructor type to a non-abstract constructor type.
3+
4+
5+
==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractClinterfaceAssignability.ts (1 errors) ====
6+
interface I {
7+
x: number;
8+
}
9+
10+
interface IConstructor {
11+
new (): I;
12+
13+
y: number;
14+
prototype: I;
15+
}
16+
17+
var I: IConstructor;
18+
19+
abstract class A {
20+
x: number;
21+
static y: number;
22+
}
23+
24+
var AA: typeof A;
25+
AA = I;
26+
27+
var AAA: typeof I;
28+
AAA = A;
29+
~~~
30+
!!! error TS2322: Type 'typeof A' is not assignable to type 'IConstructor'.
31+
!!! error TS2322: Cannot assign an abstract constructor type to a non-abstract constructor type.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//// [classAbstractClinterfaceAssignability.ts]
2+
interface I {
3+
x: number;
4+
}
5+
6+
interface IConstructor {
7+
new (): I;
8+
9+
y: number;
10+
prototype: I;
11+
}
12+
13+
var I: IConstructor;
14+
15+
abstract class A {
16+
x: number;
17+
static y: number;
18+
}
19+
20+
var AA: typeof A;
21+
AA = I;
22+
23+
var AAA: typeof I;
24+
AAA = A;
25+
26+
//// [classAbstractClinterfaceAssignability.js]
27+
var I;
28+
var A = (function () {
29+
function A() {
30+
}
31+
return A;
32+
})();
33+
var AA;
34+
AA = I;
35+
var AAA;
36+
AAA = A;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractConstructorAssignability.ts(8,5): error TS2322: Type 'typeof B' is not assignable to type 'typeof A'.
2+
Cannot assign an abstract constructor type to a non-abstract constructor type.
3+
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractConstructorAssignability.ts(10,5): error TS2322: Type 'typeof B' is not assignable to type 'typeof C'.
4+
Cannot assign an abstract constructor type to a non-abstract constructor type.
5+
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractConstructorAssignability.ts(13,1): error TS2511: Cannot create an instance of the abstract class 'B'.
6+
7+
8+
==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractConstructorAssignability.ts (3 errors) ====
9+
10+
class A {}
11+
12+
abstract class B extends A {}
13+
14+
class C extends B {}
15+
16+
var AA : typeof A = B;
17+
~~
18+
!!! error TS2322: Type 'typeof B' is not assignable to type 'typeof A'.
19+
!!! error TS2322: Cannot assign an abstract constructor type to a non-abstract constructor type.
20+
var BB : typeof B = A;
21+
var CC : typeof C = B;
22+
~~
23+
!!! error TS2322: Type 'typeof B' is not assignable to type 'typeof C'.
24+
!!! error TS2322: Cannot assign an abstract constructor type to a non-abstract constructor type.
25+
26+
new AA;
27+
new BB;
28+
~~~~~~
29+
!!! error TS2511: Cannot create an instance of the abstract class 'B'.
30+
new CC;
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//// [classAbstractConstructorAssignability.ts]
2+
3+
class A {}
4+
5+
abstract class B extends A {}
6+
7+
class C extends B {}
8+
9+
var AA : typeof A = B;
10+
var BB : typeof B = A;
11+
var CC : typeof C = B;
12+
13+
new AA;
14+
new BB;
15+
new CC;
16+
17+
//// [classAbstractConstructorAssignability.js]
18+
var __extends = (this && this.__extends) || function (d, b) {
19+
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
20+
function __() { this.constructor = d; }
21+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
22+
};
23+
var A = (function () {
24+
function A() {
25+
}
26+
return A;
27+
})();
28+
var B = (function (_super) {
29+
__extends(B, _super);
30+
function B() {
31+
_super.apply(this, arguments);
32+
}
33+
return B;
34+
})(A);
35+
var C = (function (_super) {
36+
__extends(C, _super);
37+
function C() {
38+
_super.apply(this, arguments);
39+
}
40+
return C;
41+
})(B);
42+
var AA = B;
43+
var BB = A;
44+
var CC = B;
45+
new AA;
46+
new BB;
47+
new CC;
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractExtends.ts(10,7): error TS2515: Non-abstract class 'C' does not implement inherited abstract member 'bar' from class 'B'.
2+
3+
4+
==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractExtends.ts (1 errors) ====
5+
6+
class A {
7+
foo() {}
8+
}
9+
10+
abstract class B extends A {
11+
abstract bar();
12+
}
13+
14+
class C extends B { }
15+
~
16+
!!! error TS2515: Non-abstract class 'C' does not implement inherited abstract member 'bar' from class 'B'.
17+
18+
abstract class D extends B {}
19+
20+
class E extends B {
21+
bar() {}
22+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//// [classAbstractExtends.ts]
2+
3+
class A {
4+
foo() {}
5+
}
6+
7+
abstract class B extends A {
8+
abstract bar();
9+
}
10+
11+
class C extends B { }
12+
13+
abstract class D extends B {}
14+
15+
class E extends B {
16+
bar() {}
17+
}
18+
19+
//// [classAbstractExtends.js]
20+
var __extends = (this && this.__extends) || function (d, b) {
21+
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
22+
function __() { this.constructor = d; }
23+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
24+
};
25+
var A = (function () {
26+
function A() {
27+
}
28+
A.prototype.foo = function () { };
29+
return A;
30+
})();
31+
var B = (function (_super) {
32+
__extends(B, _super);
33+
function B() {
34+
_super.apply(this, arguments);
35+
}
36+
return B;
37+
})(A);
38+
var C = (function (_super) {
39+
__extends(C, _super);
40+
function C() {
41+
_super.apply(this, arguments);
42+
}
43+
return C;
44+
})(B);
45+
var D = (function (_super) {
46+
__extends(D, _super);
47+
function D() {
48+
_super.apply(this, arguments);
49+
}
50+
return D;
51+
})(B);
52+
var E = (function (_super) {
53+
__extends(E, _super);
54+
function E() {
55+
_super.apply(this, arguments);
56+
}
57+
E.prototype.bar = function () { };
58+
return E;
59+
})(B);
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractFactoryFunction.ts(10,12): error TS2511: Cannot create an instance of the abstract class 'B'.
2+
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractFactoryFunction.ts(14,6): error TS2345: Argument of type 'typeof B' is not assignable to parameter of type 'typeof A'.
3+
Cannot assign an abstract constructor type to a non-abstract constructor type.
4+
5+
6+
==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractFactoryFunction.ts (2 errors) ====
7+
8+
class A {}
9+
abstract class B extends A {}
10+
11+
function NewA(Factory: typeof A) {
12+
return new A;
13+
}
14+
15+
function NewB(Factory: typeof B) {
16+
return new B;
17+
~~~~~
18+
!!! error TS2511: Cannot create an instance of the abstract class 'B'.
19+
}
20+
21+
NewA(A);
22+
NewA(B);
23+
~
24+
!!! error TS2345: Argument of type 'typeof B' is not assignable to parameter of type 'typeof A'.
25+
!!! error TS2345: Cannot assign an abstract constructor type to a non-abstract constructor type.
26+
27+
NewB(A);
28+
NewB(B);
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//// [classAbstractFactoryFunction.ts]
2+
3+
class A {}
4+
abstract class B extends A {}
5+
6+
function NewA(Factory: typeof A) {
7+
return new A;
8+
}
9+
10+
function NewB(Factory: typeof B) {
11+
return new B;
12+
}
13+
14+
NewA(A);
15+
NewA(B);
16+
17+
NewB(A);
18+
NewB(B);
19+
20+
//// [classAbstractFactoryFunction.js]
21+
var __extends = (this && this.__extends) || function (d, b) {
22+
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
23+
function __() { this.constructor = d; }
24+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
25+
};
26+
var A = (function () {
27+
function A() {
28+
}
29+
return A;
30+
})();
31+
var B = (function (_super) {
32+
__extends(B, _super);
33+
function B() {
34+
_super.apply(this, arguments);
35+
}
36+
return B;
37+
})(A);
38+
function NewA(Factory) {
39+
return new A;
40+
}
41+
function NewB(Factory) {
42+
return new B;
43+
}
44+
NewA(A);
45+
NewA(B);
46+
NewB(A);
47+
NewB(B);

0 commit comments

Comments
 (0)