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:
parent
b377e99958
commit
73bef22f0b
|
@ -29329,7 +29329,7 @@ namespace ts {
|
||||||
|
|
||||||
// NOTE: assignability is checked in checkClassDeclaration
|
// NOTE: assignability is checked in checkClassDeclaration
|
||||||
const baseProperties = getPropertiesOfType(baseType);
|
const baseProperties = getPropertiesOfType(baseType);
|
||||||
for (const baseProperty of baseProperties) {
|
basePropertyCheck: for (const baseProperty of baseProperties) {
|
||||||
const base = getTargetSymbol(baseProperty);
|
const base = getTargetSymbol(baseProperty);
|
||||||
|
|
||||||
if (base.flags & SymbolFlags.Prototype) {
|
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.");
|
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,
|
// 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*
|
// 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.
|
// 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),
|
// If there is no declaration for the derived class (as in the case of class expressions),
|
||||||
// then the class cannot be declared abstract.
|
// then the class cannot be declared abstract.
|
||||||
if (baseDeclarationFlags & ModifierFlags.Abstract && (!derivedClassDecl || !hasModifier(derivedClassDecl, ModifierFlags.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) {
|
if (derivedClassDecl.kind === SyntaxKind.ClassExpression) {
|
||||||
error(derivedClassDecl, Diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1,
|
error(derivedClassDecl, Diagnostics.Non_abstract_class_expression_does_not_implement_inherited_abstract_member_0_from_class_1,
|
||||||
symbolToString(baseProperty), typeToString(baseType));
|
symbolToString(baseProperty), typeToString(baseType));
|
||||||
|
@ -29397,7 +29408,6 @@ namespace ts {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function checkInheritedPropertiesAreIdentical(type: InterfaceType, typeNode: Node): boolean {
|
function checkInheritedPropertiesAreIdentical(type: InterfaceType, typeNode: Node): boolean {
|
||||||
const baseTypes = getBaseTypes(type);
|
const baseTypes = getBaseTypes(type);
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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));
|
|
@ -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))
|
||||||
|
|
|
@ -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 {}
|
||||||
|
|
|
@ -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 {}
|
Loading…
Reference in a new issue