Merge pull request #3722 from Microsoft/abstractConstructorAssignability

Abstract constructor assignability
This commit is contained in:
Arthur Ozga 2015-07-13 16:36:50 -07:00
commit 7664f3410c
25 changed files with 602 additions and 10 deletions

View File

@ -4903,9 +4903,38 @@ namespace ts {
let targetSignatures = getSignaturesOfType(target, kind);
let result = Ternary.True;
let saveErrorInfo = errorInfo;
// Because the "abstractness" of a class is the same across all construct signatures
// (internally we are checking the corresponding declaration), it is enough to perform
// the check and report an error once over all pairs of source and target construct signatures.
let sourceSig = sourceSignatures[0];
// Note that in an extends-clause, targetSignatures is stripped, so the check never proceeds.
let targetSig = targetSignatures[0];
if (sourceSig && targetSig) {
let sourceErasedSignature = getErasedSignature(sourceSig);
let targetErasedSignature = getErasedSignature(targetSig);
let sourceReturnType = sourceErasedSignature && getReturnTypeOfSignature(sourceErasedSignature);
let targetReturnType = targetErasedSignature && getReturnTypeOfSignature(targetErasedSignature);
let sourceReturnDecl = sourceReturnType && sourceReturnType.symbol && getDeclarationOfKind(sourceReturnType.symbol, SyntaxKind.ClassDeclaration);
let targetReturnDecl = targetReturnType && targetReturnType.symbol && getDeclarationOfKind(targetReturnType.symbol, SyntaxKind.ClassDeclaration);
let sourceIsAbstract = sourceReturnDecl && sourceReturnDecl.flags & NodeFlags.Abstract;
let targetIsAbstract = targetReturnDecl && targetReturnDecl.flags & NodeFlags.Abstract;
if (sourceIsAbstract && !targetIsAbstract) {
if (reportErrors) {
reportError(Diagnostics.Cannot_assign_an_abstract_constructor_type_to_a_non_abstract_constructor_type);
}
return Ternary.False;
}
}
outer: for (let t of targetSignatures) {
if (!t.hasStringLiterals || target.flags & TypeFlags.FromSignature) {
let localErrors = reportErrors;
let checkedAbstractAssignability = false;
for (let s of sourceSignatures) {
if (!s.hasStringLiterals || source.flags & TypeFlags.FromSignature) {
let related = signatureRelatedTo(s, t, localErrors);
@ -5012,10 +5041,11 @@ namespace ts {
return Ternary.False;
}
let t = getReturnTypeOfSignature(target);
if (t === voidType) return result;
let s = getReturnTypeOfSignature(source);
return result & isRelatedTo(s, t, reportErrors);
let targetReturnType = getReturnTypeOfSignature(target);
if (targetReturnType === voidType) return result;
let sourceReturnType = getReturnTypeOfSignature(source);
return result & isRelatedTo(sourceReturnType, targetReturnType, reportErrors);
}
function signaturesIdenticalTo(source: Type, target: Type, kind: SignatureKind): Ternary {

View File

@ -407,7 +407,7 @@ namespace ts {
Classes_containing_abstract_methods_must_be_marked_abstract: { code: 2514, category: DiagnosticCategory.Error, key: "Classes containing abstract methods must be marked abstract." },
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}'." },
All_declarations_of_an_abstract_method_must_be_consecutive: { code: 2516, category: DiagnosticCategory.Error, key: "All declarations of an abstract method must be consecutive." },
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" },
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." },
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." },
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." },
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." },

View File

@ -1617,7 +1617,7 @@
"category": "Error",
"code": 2516
},
"Constructor objects of abstract type cannot be assigned to constructor objects of non-abstract type": {
"Cannot assign an abstract constructor type to a non-abstract constructor type.": {
"category": "Error",
"code":2517
},

View File

@ -0,0 +1,31 @@
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractClinterfaceAssignability.ts(23,1): error TS2322: Type 'typeof A' is not assignable to type 'IConstructor'.
Cannot assign an abstract constructor type to a non-abstract constructor type.
==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractClinterfaceAssignability.ts (1 errors) ====
interface I {
x: number;
}
interface IConstructor {
new (): I;
y: number;
prototype: I;
}
var I: IConstructor;
abstract class A {
x: number;
static y: number;
}
var AA: typeof A;
AA = I;
var AAA: typeof I;
AAA = A;
~~~
!!! error TS2322: Type 'typeof A' is not assignable to type 'IConstructor'.
!!! error TS2322: Cannot assign an abstract constructor type to a non-abstract constructor type.

View File

@ -0,0 +1,36 @@
//// [classAbstractClinterfaceAssignability.ts]
interface I {
x: number;
}
interface IConstructor {
new (): I;
y: number;
prototype: I;
}
var I: IConstructor;
abstract class A {
x: number;
static y: number;
}
var AA: typeof A;
AA = I;
var AAA: typeof I;
AAA = A;
//// [classAbstractClinterfaceAssignability.js]
var I;
var A = (function () {
function A() {
}
return A;
})();
var AA;
AA = I;
var AAA;
AAA = A;

View File

@ -0,0 +1,30 @@
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractConstructorAssignability.ts(8,5): error TS2322: Type 'typeof B' is not assignable to type 'typeof A'.
Cannot assign an abstract constructor type to a non-abstract constructor type.
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractConstructorAssignability.ts(10,5): error TS2322: Type 'typeof B' is not assignable to type 'typeof C'.
Cannot assign an abstract constructor type to a non-abstract constructor type.
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractConstructorAssignability.ts(13,1): error TS2511: Cannot create an instance of the abstract class 'B'.
==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractConstructorAssignability.ts (3 errors) ====
class A {}
abstract class B extends A {}
class C extends B {}
var AA : typeof A = B;
~~
!!! error TS2322: Type 'typeof B' is not assignable to type 'typeof A'.
!!! error TS2322: Cannot assign an abstract constructor type to a non-abstract constructor type.
var BB : typeof B = A;
var CC : typeof C = B;
~~
!!! error TS2322: Type 'typeof B' is not assignable to type 'typeof C'.
!!! error TS2322: Cannot assign an abstract constructor type to a non-abstract constructor type.
new AA;
new BB;
~~~~~~
!!! error TS2511: Cannot create an instance of the abstract class 'B'.
new CC;

View File

@ -0,0 +1,47 @@
//// [classAbstractConstructorAssignability.ts]
class A {}
abstract class B extends A {}
class C extends B {}
var AA : typeof A = B;
var BB : typeof B = A;
var CC : typeof C = B;
new AA;
new BB;
new CC;
//// [classAbstractConstructorAssignability.js]
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var A = (function () {
function A() {
}
return A;
})();
var B = (function (_super) {
__extends(B, _super);
function B() {
_super.apply(this, arguments);
}
return B;
})(A);
var C = (function (_super) {
__extends(C, _super);
function C() {
_super.apply(this, arguments);
}
return C;
})(B);
var AA = B;
var BB = A;
var CC = B;
new AA;
new BB;
new CC;

View File

@ -0,0 +1,22 @@
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'.
==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractExtends.ts (1 errors) ====
class A {
foo() {}
}
abstract class B extends A {
abstract bar();
}
class C extends B { }
~
!!! error TS2515: Non-abstract class 'C' does not implement inherited abstract member 'bar' from class 'B'.
abstract class D extends B {}
class E extends B {
bar() {}
}

View File

@ -0,0 +1,59 @@
//// [classAbstractExtends.ts]
class A {
foo() {}
}
abstract class B extends A {
abstract bar();
}
class C extends B { }
abstract class D extends B {}
class E extends B {
bar() {}
}
//// [classAbstractExtends.js]
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var A = (function () {
function A() {
}
A.prototype.foo = function () { };
return A;
})();
var B = (function (_super) {
__extends(B, _super);
function B() {
_super.apply(this, arguments);
}
return B;
})(A);
var C = (function (_super) {
__extends(C, _super);
function C() {
_super.apply(this, arguments);
}
return C;
})(B);
var D = (function (_super) {
__extends(D, _super);
function D() {
_super.apply(this, arguments);
}
return D;
})(B);
var E = (function (_super) {
__extends(E, _super);
function E() {
_super.apply(this, arguments);
}
E.prototype.bar = function () { };
return E;
})(B);

View File

@ -0,0 +1,28 @@
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractFactoryFunction.ts(10,12): error TS2511: Cannot create an instance of the abstract class 'B'.
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'.
Cannot assign an abstract constructor type to a non-abstract constructor type.
==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractFactoryFunction.ts (2 errors) ====
class A {}
abstract class B extends A {}
function NewA(Factory: typeof A) {
return new A;
}
function NewB(Factory: typeof B) {
return new B;
~~~~~
!!! error TS2511: Cannot create an instance of the abstract class 'B'.
}
NewA(A);
NewA(B);
~
!!! error TS2345: Argument of type 'typeof B' is not assignable to parameter of type 'typeof A'.
!!! error TS2345: Cannot assign an abstract constructor type to a non-abstract constructor type.
NewB(A);
NewB(B);

View File

@ -0,0 +1,47 @@
//// [classAbstractFactoryFunction.ts]
class A {}
abstract class B extends A {}
function NewA(Factory: typeof A) {
return new A;
}
function NewB(Factory: typeof B) {
return new B;
}
NewA(A);
NewA(B);
NewB(A);
NewB(B);
//// [classAbstractFactoryFunction.js]
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var A = (function () {
function A() {
}
return A;
})();
var B = (function (_super) {
__extends(B, _super);
function B() {
_super.apply(this, arguments);
}
return B;
})(A);
function NewA(Factory) {
return new A;
}
function NewB(Factory) {
return new B;
}
NewA(A);
NewA(B);
NewB(A);
NewB(B);

View File

@ -1,10 +1,14 @@
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations1.ts(8,1): error TS2511: Cannot create an instance of the abstract class 'A'.
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations1.ts(9,1): error TS2511: Cannot create an instance of the abstract class 'A'.
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations1.ts(11,1): error TS2511: Cannot create an instance of the abstract class 'C'.
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations1.ts(12,1): error TS2511: Cannot create an instance of the abstract class 'A'.
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations1.ts(13,1): error TS2511: Cannot create an instance of the abstract class 'A'.
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations1.ts(15,1): error TS2511: Cannot create an instance of the abstract class 'C'.
==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations1.ts (3 errors) ====
//
// Calling new with (non)abstract classes.
//
abstract class A {}
class B extends A {}

View File

@ -1,5 +1,9 @@
//// [classAbstractInstantiations1.ts]
//
// Calling new with (non)abstract classes.
//
abstract class A {}
class B extends A {}
@ -21,6 +25,9 @@ c = new B;
//// [classAbstractInstantiations1.js]
//
// Calling new with (non)abstract classes.
//
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }

View File

@ -1,4 +1,6 @@
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts(10,1): error TS2511: Cannot create an instance of the abstract class 'B'.
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts(13,5): error TS2322: Type 'typeof B' is not assignable to type 'typeof A'.
Cannot assign an abstract constructor type to a non-abstract constructor type.
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts(17,5): error TS2511: Cannot create an instance of the abstract class 'B'.
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts(21,1): error TS2511: Cannot create an instance of the abstract class 'B'.
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts(26,7): error TS2515: Non-abstract class 'C' does not implement inherited abstract member 'bar' from class 'B'.
@ -7,7 +9,7 @@ tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbst
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts(50,5): error TS1244: Abstract methods can only appear within an abstract class.
==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts (7 errors) ====
==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractInstantiations2.ts (8 errors) ====
class A {
// ...
}
@ -23,6 +25,9 @@ tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbst
var BB: typeof B = B;
var AA: typeof A = BB; // error, AA is not of abstract type.
~~
!!! error TS2322: Type 'typeof B' is not assignable to type 'typeof A'.
!!! error TS2322: Cannot assign an abstract constructor type to a non-abstract constructor type.
new AA;
function constructB(Factory : typeof B) {

View File

@ -0,0 +1,19 @@
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractMethodInNonAbstractClass.ts(2,5): error TS1244: Abstract methods can only appear within an abstract class.
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractMethodInNonAbstractClass.ts(6,5): error TS1244: Abstract methods can only appear within an abstract class.
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractMethodInNonAbstractClass.ts(6,5): error TS1245: Method 'foo' cannot have an implementation because it is marked abstract.
==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractMethodInNonAbstractClass.ts (3 errors) ====
class A {
abstract foo();
~~~~~~~~
!!! error TS1244: Abstract methods can only appear within an abstract class.
}
class B {
abstract foo() {}
~~~~~~~~
!!! error TS1244: Abstract methods can only appear within an abstract class.
~~~~~~~~~~~~~~~~~
!!! error TS1245: Method 'foo' cannot have an implementation because it is marked abstract.
}

View File

@ -0,0 +1,21 @@
//// [classAbstractMethodInNonAbstractClass.ts]
class A {
abstract foo();
}
class B {
abstract foo() {}
}
//// [classAbstractMethodInNonAbstractClass.js]
var A = (function () {
function A() {
}
return A;
})();
var B = (function () {
function B() {
}
B.prototype.foo = function () { };
return B;
})();

View File

@ -0,0 +1,29 @@
tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractOverrideWithAbstract.ts(19,7): error TS2515: Non-abstract class 'CC' does not implement inherited abstract member 'foo' from class 'BB'.
==== tests/cases/conformance/classes/classDeclarations/classAbstractKeyword/classAbstractOverrideWithAbstract.ts (1 errors) ====
class A {
foo() {}
}
abstract class B extends A {
abstract foo();
}
abstract class AA {
foo() {}
abstract bar();
}
abstract class BB extends AA {
abstract foo();
bar () {}
}
class CC extends BB {} // error
~~
!!! error TS2515: Non-abstract class 'CC' does not implement inherited abstract member 'foo' from class 'BB'.
class DD extends BB {
foo() {}
}

View File

@ -0,0 +1,73 @@
//// [classAbstractOverrideWithAbstract.ts]
class A {
foo() {}
}
abstract class B extends A {
abstract foo();
}
abstract class AA {
foo() {}
abstract bar();
}
abstract class BB extends AA {
abstract foo();
bar () {}
}
class CC extends BB {} // error
class DD extends BB {
foo() {}
}
//// [classAbstractOverrideWithAbstract.js]
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var A = (function () {
function A() {
}
A.prototype.foo = function () { };
return A;
})();
var B = (function (_super) {
__extends(B, _super);
function B() {
_super.apply(this, arguments);
}
return B;
})(A);
var AA = (function () {
function AA() {
}
AA.prototype.foo = function () { };
return AA;
})();
var BB = (function (_super) {
__extends(BB, _super);
function BB() {
_super.apply(this, arguments);
}
BB.prototype.bar = function () { };
return BB;
})(AA);
var CC = (function (_super) {
__extends(CC, _super);
function CC() {
_super.apply(this, arguments);
}
return CC;
})(BB); // error
var DD = (function (_super) {
__extends(DD, _super);
function DD() {
_super.apply(this, arguments);
}
DD.prototype.foo = function () { };
return DD;
})(BB);

View File

@ -0,0 +1,23 @@
interface I {
x: number;
}
interface IConstructor {
new (): I;
y: number;
prototype: I;
}
var I: IConstructor;
abstract class A {
x: number;
static y: number;
}
var AA: typeof A;
AA = I;
var AAA: typeof I;
AAA = A;

View File

@ -0,0 +1,14 @@
class A {}
abstract class B extends A {}
class C extends B {}
var AA : typeof A = B;
var BB : typeof B = A;
var CC : typeof C = B;
new AA;
new BB;
new CC;

View File

@ -0,0 +1,16 @@
class A {
foo() {}
}
abstract class B extends A {
abstract bar();
}
class C extends B { }
abstract class D extends B {}
class E extends B {
bar() {}
}

View File

@ -0,0 +1,17 @@
class A {}
abstract class B extends A {}
function NewA(Factory: typeof A) {
return new A;
}
function NewB(Factory: typeof B) {
return new B;
}
NewA(A);
NewA(B);
NewB(A);
NewB(B);

View File

@ -1,4 +1,8 @@
//
// Calling new with (non)abstract classes.
//
abstract class A {}
class B extends A {}

View File

@ -0,0 +1,7 @@
class A {
abstract foo();
}
class B {
abstract foo() {}
}

View File

@ -0,0 +1,23 @@
class A {
foo() {}
}
abstract class B extends A {
abstract foo();
}
abstract class AA {
foo() {}
abstract bar();
}
abstract class BB extends AA {
abstract foo();
bar () {}
}
class CC extends BB {} // error
class DD extends BB {
foo() {}
}