Skip to content

Commit 3a08af1

Browse files
committed
Check for class expressions as well as class declarations
1 parent 5542e39 commit 3a08af1

6 files changed

Lines changed: 70 additions & 5 deletions

src/compiler/checker.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12625,6 +12625,10 @@ namespace ts {
1262512625
return s.flags & SymbolFlags.Instantiated ? getSymbolLinks(s).target : s;
1262612626
}
1262712627

12628+
function getClassLikeDeclarationOfSymbol(symbol: Symbol): Declaration {
12629+
return forEach(symbol.declarations, d => isClassLike(d) ? d : undefined);
12630+
}
12631+
1262812632
function checkKindsOfPropertyMemberOverrides(type: InterfaceType, baseType: ObjectType): void {
1262912633

1263012634
// TypeScript 1.0 spec (April 2014): 8.2.3
@@ -12662,14 +12666,20 @@ namespace ts {
1266212666
if (derived === base) {
1266312667
// derived class inherits base without override/redeclaration
1266412668

12665-
let derivedClassDecl = getDeclarationOfKind(type.symbol, SyntaxKind.ClassDeclaration);
12669+
let derivedClassDecl = getClassLikeDeclarationOfSymbol(type.symbol);
1266612670

1266712671
// It is an error to inherit an abstract member without implementing it or being declared abstract.
1266812672
// If there is no declaration for the derived class (as in the case of class expressions),
1266912673
// then the class cannot be declared abstract.
12670-
if ( baseDeclarationFlags & NodeFlags.Abstract && (!derivedClassDecl || !(derivedClassDecl.flags & NodeFlags.Abstract))) {
12671-
error(derivedClassDecl, Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2,
12672-
typeToString(type), symbolToString(baseProperty), typeToString(baseType));
12674+
if (baseDeclarationFlags & NodeFlags.Abstract && (!derivedClassDecl || !(derivedClassDecl.flags & NodeFlags.Abstract))) {
12675+
if (derivedClassDecl.kind === SyntaxKind.ClassExpression) {
12676+
error(derivedClassDecl, Diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1,
12677+
symbolToString(baseProperty), typeToString(baseType));
12678+
}
12679+
else {
12680+
error(derivedClassDecl, Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2,
12681+
typeToString(type), symbolToString(baseProperty), typeToString(baseType));
12682+
}
1267312683
}
1267412684
}
1267512685
else {

src/compiler/diagnosticInformationMap.generated.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,7 @@ namespace ts {
427427
Cannot_emit_namespaced_JSX_elements_in_React: { code: 2650, category: DiagnosticCategory.Error, key: "Cannot emit namespaced JSX elements in React" },
428428
A_member_initializer_in_a_enum_declaration_cannot_reference_members_declared_after_it_including_members_defined_in_other_enums: { code: 2651, category: DiagnosticCategory.Error, key: "A member initializer in a enum declaration cannot reference members declared after it, including members defined in other enums." },
429429
Merged_declaration_0_cannot_include_a_default_export_declaration_Consider_adding_a_separate_export_default_0_declaration_instead: { code: 2652, category: DiagnosticCategory.Error, key: "Merged declaration '{0}' cannot include a default export declaration. Consider adding a separate 'export default {0}' declaration instead." },
430+
Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1: { code: 2653, category: DiagnosticCategory.Error, key: "Non-abstract class expression does not implement inherited abstract member '{0}' from class '{1}'." },
430431
Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." },
431432
Type_parameter_0_of_exported_class_has_or_is_using_private_name_1: { code: 4002, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported class has or is using private name '{1}'." },
432433
Type_parameter_0_of_exported_interface_has_or_is_using_private_name_1: { code: 4004, category: DiagnosticCategory.Error, key: "Type parameter '{0}' of exported interface has or is using private name '{1}'." },

src/compiler/diagnosticMessages.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1692,11 +1692,16 @@
16921692
"A member initializer in a enum declaration cannot reference members declared after it, including members defined in other enums.": {
16931693
"category": "Error",
16941694
"code": 2651
1695-
},
1695+
},
16961696
"Merged declaration '{0}' cannot include a default export declaration. Consider adding a separate 'export default {0}' declaration instead.": {
16971697
"category": "Error",
16981698
"code": 2652
16991699
},
1700+
"Non-abstract class expression does not implement inherited abstract member '{0}' from class '{1}'.": {
1701+
"category": "Error",
1702+
"code": 2653
1703+
},
1704+
17001705
"Import declaration '{0}' is using private name '{1}'.": {
17011706
"category": "Error",
17021707
"code": 4000
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
tests/cases/compiler/classExpressionExtendingAbstractClass.ts(5,9): error TS2653: Non-abstract class expression does not implement inherited abstract member 'foo' from class 'A'.
2+
3+
4+
==== tests/cases/compiler/classExpressionExtendingAbstractClass.ts (1 errors) ====
5+
abstract class A {
6+
abstract foo(): void;
7+
}
8+
9+
var C = class extends A { // no error reported!
10+
~~~~~
11+
!!! error TS2653: Non-abstract class expression does not implement inherited abstract member 'foo' from class 'A'.
12+
};
13+
14+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//// [classExpressionExtendingAbstractClass.ts]
2+
abstract class A {
3+
abstract foo(): void;
4+
}
5+
6+
var C = class extends A { // no error reported!
7+
};
8+
9+
10+
11+
//// [classExpressionExtendingAbstractClass.js]
12+
var __extends = (this && this.__extends) || function (d, b) {
13+
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
14+
function __() { this.constructor = d; }
15+
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
16+
};
17+
var A = (function () {
18+
function A() {
19+
}
20+
return A;
21+
})();
22+
var C = (function (_super) {
23+
__extends(class_1, _super);
24+
function class_1() {
25+
_super.apply(this, arguments);
26+
}
27+
return class_1;
28+
})(A);
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
abstract class A {
2+
abstract foo(): void;
3+
}
4+
5+
var C = class extends A { // no error reported!
6+
};
7+

0 commit comments

Comments
 (0)