Skip to content
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
28 changes: 19 additions & 9 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5105,8 +5105,8 @@ namespace ts {
function abstractSignatureRelatedTo(source: Type, sourceSig: Signature, target: Type, targetSig: Signature) {
if (sourceSig && targetSig) {

let sourceDecl = source.symbol && getDeclarationOfKind(source.symbol, SyntaxKind.ClassDeclaration);
let targetDecl = target.symbol && getDeclarationOfKind(target.symbol, SyntaxKind.ClassDeclaration);
let sourceDecl = source.symbol && getClassLikeDeclarationOfSymbol(source.symbol);
let targetDecl = target.symbol && getClassLikeDeclarationOfSymbol(target.symbol);

if (!sourceDecl) {
// If the source object isn't itself a class declaration, it can be freely assigned, regardless
Expand All @@ -5120,8 +5120,8 @@ namespace ts {
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 sourceReturnDecl = sourceReturnType && sourceReturnType.symbol && getClassLikeDeclarationOfSymbol(sourceReturnType.symbol);
let targetReturnDecl = targetReturnType && targetReturnType.symbol && getClassLikeDeclarationOfSymbol(targetReturnType.symbol);
let sourceIsAbstract = sourceReturnDecl && sourceReturnDecl.flags & NodeFlags.Abstract;
let targetIsAbstract = targetReturnDecl && targetReturnDecl.flags & NodeFlags.Abstract;

Expand Down Expand Up @@ -8873,7 +8873,7 @@ namespace ts {
// Note, only class declarations can be declared abstract.
// In the case of a merged class-module or class-interface declaration,
// only the class declaration node will have the Abstract flag set.
let valueDecl = expressionType.symbol && getDeclarationOfKind(expressionType.symbol, SyntaxKind.ClassDeclaration);
let valueDecl = expressionType.symbol && getClassLikeDeclarationOfSymbol(expressionType.symbol);
if (valueDecl && valueDecl.flags & NodeFlags.Abstract) {
error(node, Diagnostics.Cannot_create_an_instance_of_the_abstract_class_0, declarationNameToString(valueDecl.name));
return resolveErrorCall(node);
Expand Down Expand Up @@ -12625,6 +12625,10 @@ namespace ts {
return s.flags & SymbolFlags.Instantiated ? getSymbolLinks(s).target : s;
}

function getClassLikeDeclarationOfSymbol(symbol: Symbol): Declaration {
return forEach(symbol.declarations, d => isClassLike(d) ? d : undefined);
}

function checkKindsOfPropertyMemberOverrides(type: InterfaceType, baseType: ObjectType): void {

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

let derivedClassDecl = getDeclarationOfKind(type.symbol, SyntaxKind.ClassDeclaration);
let derivedClassDecl = getClassLikeDeclarationOfSymbol(type.symbol);

// It is an error to inherit an abstract member without implementing it or being declared abstract.
// If there is no declaration for the derived class (as in the case of class expressions),
// then the class cannot be declared abstract.
if ( baseDeclarationFlags & NodeFlags.Abstract && (!derivedClassDecl || !(derivedClassDecl.flags & NodeFlags.Abstract))) {
error(derivedClassDecl, Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2,
typeToString(type), symbolToString(baseProperty), typeToString(baseType));
if (baseDeclarationFlags & NodeFlags.Abstract && (!derivedClassDecl || !(derivedClassDecl.flags & NodeFlags.Abstract))) {
if (derivedClassDecl.kind === SyntaxKind.ClassExpression) {
error(derivedClassDecl, Diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1,
symbolToString(baseProperty), typeToString(baseType));
}
else {
error(derivedClassDecl, Diagnostics.Non_abstract_class_0_does_not_implement_inherited_abstract_member_1_from_class_2,
typeToString(type), symbolToString(baseProperty), typeToString(baseType));
}
}
}
else {
Expand Down
1 change: 1 addition & 0 deletions src/compiler/diagnosticInformationMap.generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ namespace ts {
Cannot_emit_namespaced_JSX_elements_in_React: { code: 2650, category: DiagnosticCategory.Error, key: "Cannot emit namespaced JSX elements in React" },
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." },
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." },
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}'." },
Import_declaration_0_is_using_private_name_1: { code: 4000, category: DiagnosticCategory.Error, key: "Import declaration '{0}' is using private name '{1}'." },
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}'." },
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}'." },
Expand Down
7 changes: 6 additions & 1 deletion src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1692,11 +1692,16 @@
"A member initializer in a enum declaration cannot reference members declared after it, including members defined in other enums.": {
"category": "Error",
"code": 2651
},
},
"Merged declaration '{0}' cannot include a default export declaration. Consider adding a separate 'export default {0}' declaration instead.": {
"category": "Error",
"code": 2652
},
"Non-abstract class expression does not implement inherited abstract member '{0}' from class '{1}'.": {
"category": "Error",
"code": 2653
},

"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
"code": 4000
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
tests/cases/compiler/classExpressionExtendingAbstractClass.ts(5,9): error TS2653: Non-abstract class expression does not implement inherited abstract member 'foo' from class 'A'.


==== tests/cases/compiler/classExpressionExtendingAbstractClass.ts (1 errors) ====
abstract class A {
abstract foo(): void;
}

var C = class extends A { // no error reported!
~~~~~
!!! error TS2653: Non-abstract class expression does not implement inherited abstract member 'foo' from class 'A'.
};


28 changes: 28 additions & 0 deletions tests/baselines/reference/classExpressionExtendingAbstractClass.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
//// [classExpressionExtendingAbstractClass.ts]
abstract class A {
abstract foo(): void;
}

var C = class extends A { // no error reported!
};



//// [classExpressionExtendingAbstractClass.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 C = (function (_super) {
__extends(class_1, _super);
function class_1() {
_super.apply(this, arguments);
}
return class_1;
})(A);
7 changes: 7 additions & 0 deletions tests/cases/compiler/classExpressionExtendingAbstractClass.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
abstract class A {
abstract foo(): void;
}

var C = class extends A { // no error reported!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment is misleading, can you remove it?

};