A merged interface with an inherited member should satisfy an abstract base class member (#32539)

* A merged interface with an inherited member should satisfy an abstract base class member

* Tighten up comments and names
This commit is contained in:
Andrew Branch 2019-08-01 09:34:11 -07:00 committed by GitHub
parent b377e99958
commit 73bef22f0b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 236 additions and 46 deletions

View file

@ -29329,7 +29329,7 @@ namespace ts {
// NOTE: assignability is checked in checkClassDeclaration
const baseProperties = getPropertiesOfType(baseType);
for (const baseProperty of baseProperties) {
basePropertyCheck: for (const baseProperty of baseProperties) {
const base = getTargetSymbol(baseProperty);
if (base.flags & SymbolFlags.Prototype) {
@ -29341,7 +29341,6 @@ namespace ts {
Debug.assert(!!derived, "derived should point to something, even if it is the base class' declaration.");
if (derived) {
// In order to resolve whether the inherited method was overridden in the base class or not,
// we compare the Symbols obtained. Since getTargetSymbol returns the symbol on the *uninstantiated*
// type declaration, derived and base resolve to the same symbol even in the case of generic classes.
@ -29354,6 +29353,18 @@ namespace ts {
// 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 & ModifierFlags.Abstract && (!derivedClassDecl || !hasModifier(derivedClassDecl, ModifierFlags.Abstract))) {
// Searches other base types for a declaration that would satisfy the inherited abstract member.
// (The class may have more than one base type via declaration merging with an interface with the
// same name.)
for (const otherBaseType of getBaseTypes(type)) {
if (otherBaseType === baseType) continue;
const baseSymbol = getPropertyOfObjectType(otherBaseType, base.escapedName);
const derivedElsewhere = baseSymbol && getTargetSymbol(baseSymbol);
if (derivedElsewhere && derivedElsewhere !== base) {
continue basePropertyCheck;
}
}
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));
@ -29397,7 +29408,6 @@ namespace ts {
}
}
}
}
function checkInheritedPropertiesAreIdentical(type: InterfaceType, typeNode: Node): boolean {
const baseTypes = getBaseTypes(type);

View file

@ -0,0 +1,28 @@
tests/cases/conformance/interfaces/declarationMerging/mergedInheritedMembersSatisfyAbstractBase.ts(19,11): error TS2320: Interface 'IncorrectlyExtends' cannot simultaneously extend types 'BaseClass' and 'IncorrectGetters'.
Named property 'bar' of types 'BaseClass' and 'IncorrectGetters' are not identical.
==== tests/cases/conformance/interfaces/declarationMerging/mergedInheritedMembersSatisfyAbstractBase.ts (1 errors) ====
abstract class BaseClass {
abstract bar: number;
}
class Broken extends BaseClass {}
// declaration merging should satisfy abstract bar
interface IGetters {
bar: number;
}
interface Broken extends IGetters {}
new Broken().bar
class IncorrectlyExtends extends BaseClass {}
interface IncorrectGetters {
bar: string;
}
interface IncorrectlyExtends extends IncorrectGetters {}
~~~~~~~~~~~~~~~~~~
!!! error TS2320: Interface 'IncorrectlyExtends' cannot simultaneously extend types 'BaseClass' and 'IncorrectGetters'.
!!! error TS2320: Named property 'bar' of types 'BaseClass' and 'IncorrectGetters' are not identical.

View file

@ -0,0 +1,56 @@
//// [mergedInheritedMembersSatisfyAbstractBase.ts]
abstract class BaseClass {
abstract bar: number;
}
class Broken extends BaseClass {}
// declaration merging should satisfy abstract bar
interface IGetters {
bar: number;
}
interface Broken extends IGetters {}
new Broken().bar
class IncorrectlyExtends extends BaseClass {}
interface IncorrectGetters {
bar: string;
}
interface IncorrectlyExtends extends IncorrectGetters {}
//// [mergedInheritedMembersSatisfyAbstractBase.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 (b.hasOwnProperty(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 BaseClass = /** @class */ (function () {
function BaseClass() {
}
return BaseClass;
}());
var Broken = /** @class */ (function (_super) {
__extends(Broken, _super);
function Broken() {
return _super !== null && _super.apply(this, arguments) || this;
}
return Broken;
}(BaseClass));
new Broken().bar;
var IncorrectlyExtends = /** @class */ (function (_super) {
__extends(IncorrectlyExtends, _super);
function IncorrectlyExtends() {
return _super !== null && _super.apply(this, arguments) || this;
}
return IncorrectlyExtends;
}(BaseClass));

View file

@ -0,0 +1,42 @@
=== tests/cases/conformance/interfaces/declarationMerging/mergedInheritedMembersSatisfyAbstractBase.ts ===
abstract class BaseClass {
>BaseClass : Symbol(BaseClass, Decl(mergedInheritedMembersSatisfyAbstractBase.ts, 0, 0))
abstract bar: number;
>bar : Symbol(BaseClass.bar, Decl(mergedInheritedMembersSatisfyAbstractBase.ts, 0, 26))
}
class Broken extends BaseClass {}
>Broken : Symbol(Broken, Decl(mergedInheritedMembersSatisfyAbstractBase.ts, 2, 1), Decl(mergedInheritedMembersSatisfyAbstractBase.ts, 9, 1))
>BaseClass : Symbol(BaseClass, Decl(mergedInheritedMembersSatisfyAbstractBase.ts, 0, 0))
// declaration merging should satisfy abstract bar
interface IGetters {
>IGetters : Symbol(IGetters, Decl(mergedInheritedMembersSatisfyAbstractBase.ts, 4, 33))
bar: number;
>bar : Symbol(IGetters.bar, Decl(mergedInheritedMembersSatisfyAbstractBase.ts, 7, 20))
}
interface Broken extends IGetters {}
>Broken : Symbol(Broken, Decl(mergedInheritedMembersSatisfyAbstractBase.ts, 2, 1), Decl(mergedInheritedMembersSatisfyAbstractBase.ts, 9, 1))
>IGetters : Symbol(IGetters, Decl(mergedInheritedMembersSatisfyAbstractBase.ts, 4, 33))
new Broken().bar
>new Broken().bar : Symbol(BaseClass.bar, Decl(mergedInheritedMembersSatisfyAbstractBase.ts, 0, 26))
>Broken : Symbol(Broken, Decl(mergedInheritedMembersSatisfyAbstractBase.ts, 2, 1), Decl(mergedInheritedMembersSatisfyAbstractBase.ts, 9, 1))
>bar : Symbol(BaseClass.bar, Decl(mergedInheritedMembersSatisfyAbstractBase.ts, 0, 26))
class IncorrectlyExtends extends BaseClass {}
>IncorrectlyExtends : Symbol(IncorrectlyExtends, Decl(mergedInheritedMembersSatisfyAbstractBase.ts, 12, 16), Decl(mergedInheritedMembersSatisfyAbstractBase.ts, 17, 1))
>BaseClass : Symbol(BaseClass, Decl(mergedInheritedMembersSatisfyAbstractBase.ts, 0, 0))
interface IncorrectGetters {
>IncorrectGetters : Symbol(IncorrectGetters, Decl(mergedInheritedMembersSatisfyAbstractBase.ts, 14, 45))
bar: string;
>bar : Symbol(IncorrectGetters.bar, Decl(mergedInheritedMembersSatisfyAbstractBase.ts, 15, 28))
}
interface IncorrectlyExtends extends IncorrectGetters {}
>IncorrectlyExtends : Symbol(IncorrectlyExtends, Decl(mergedInheritedMembersSatisfyAbstractBase.ts, 12, 16), Decl(mergedInheritedMembersSatisfyAbstractBase.ts, 17, 1))
>IncorrectGetters : Symbol(IncorrectGetters, Decl(mergedInheritedMembersSatisfyAbstractBase.ts, 14, 45))

View file

@ -0,0 +1,35 @@
=== tests/cases/conformance/interfaces/declarationMerging/mergedInheritedMembersSatisfyAbstractBase.ts ===
abstract class BaseClass {
>BaseClass : BaseClass
abstract bar: number;
>bar : number
}
class Broken extends BaseClass {}
>Broken : Broken
>BaseClass : BaseClass
// declaration merging should satisfy abstract bar
interface IGetters {
bar: number;
>bar : number
}
interface Broken extends IGetters {}
new Broken().bar
>new Broken().bar : number
>new Broken() : Broken
>Broken : typeof Broken
>bar : number
class IncorrectlyExtends extends BaseClass {}
>IncorrectlyExtends : IncorrectlyExtends
>BaseClass : BaseClass
interface IncorrectGetters {
bar: string;
>bar : string
}
interface IncorrectlyExtends extends IncorrectGetters {}

View file

@ -0,0 +1,19 @@
abstract class BaseClass {
abstract bar: number;
}
class Broken extends BaseClass {}
// declaration merging should satisfy abstract bar
interface IGetters {
bar: number;
}
interface Broken extends IGetters {}
new Broken().bar
class IncorrectlyExtends extends BaseClass {}
interface IncorrectGetters {
bar: string;
}
interface IncorrectlyExtends extends IncorrectGetters {}