Error accessing abstract property in constructor of abstract subclass

Fixes: #26411
This commit is contained in:
Klaus Meinhardt 2018-09-06 08:52:53 +02:00
parent c62920ac81
commit c203c27f00
6 changed files with 362 additions and 11 deletions

View file

@ -17986,7 +17986,7 @@ namespace ts {
// Referencing abstract properties within their own constructors is not allowed
if ((flags & ModifierFlags.Abstract) && isThisProperty(node) && symbolHasNonMethodDeclaration(prop)) {
const declaringClassDeclaration = getClassLikeDeclarationOfSymbol(getParentOfSymbol(prop)!);
if (declaringClassDeclaration && isNodeUsedDuringClassInitialization(node, declaringClassDeclaration)) {
if (declaringClassDeclaration && isNodeUsedDuringClassInitialization(node)) {
error(errorNode, Diagnostics.Abstract_property_0_in_class_1_cannot_be_accessed_in_the_constructor, symbolToString(prop), getTextOfIdentifierOrLiteral(declaringClassDeclaration.name!)); // TODO: GH#18217
return false;
}
@ -27120,12 +27120,12 @@ namespace ts {
return result;
}
function isNodeUsedDuringClassInitialization(node: Node, classDeclaration: ClassLikeDeclaration) {
function isNodeUsedDuringClassInitialization(node: Node) {
return !!findAncestor(node, element => {
if ((isConstructorDeclaration(element) && nodeIsPresent(element.body) || isPropertyDeclaration(element)) && element.parent === classDeclaration) {
if (isConstructorDeclaration(element) && nodeIsPresent(element.body) || isPropertyDeclaration(element)) {
return true;
}
else if (element === classDeclaration || isFunctionLikeDeclaration(element)) {
else if (isClassLike(element) || isFunctionLikeDeclaration(element)) {
return "quit";
}

View file

@ -2,9 +2,10 @@ tests/cases/compiler/abstractPropertyInConstructor.ts(4,24): error TS2715: Abstr
tests/cases/compiler/abstractPropertyInConstructor.ts(7,18): error TS2715: Abstract property 'prop' in class 'AbstractClass' cannot be accessed in the constructor.
tests/cases/compiler/abstractPropertyInConstructor.ts(9,14): error TS2715: Abstract property 'cb' in class 'AbstractClass' cannot be accessed in the constructor.
tests/cases/compiler/abstractPropertyInConstructor.ts(25,18): error TS2715: Abstract property 'prop' in class 'AbstractClass' cannot be accessed in the constructor.
tests/cases/compiler/abstractPropertyInConstructor.ts(39,22): error TS2715: Abstract property 'prop' in class 'AbstractClass' cannot be accessed in the constructor.
==== tests/cases/compiler/abstractPropertyInConstructor.ts (4 errors) ====
==== tests/cases/compiler/abstractPropertyInConstructor.ts (5 errors) ====
abstract class AbstractClass {
constructor(str: string, other: AbstractClass) {
this.method(parseInt(str));
@ -45,6 +46,38 @@ tests/cases/compiler/abstractPropertyInConstructor.ts(25,18): error TS2715: Abst
}
}
abstract class DerivedAbstractClass extends AbstractClass {
cb = (s: string) => {};
constructor(str: string, other: AbstractClass, yetAnother: DerivedAbstractClass) {
super(str, other);
// there is no implementation of 'prop' in any base class
this.cb(this.prop.toLowerCase());
~~~~
!!! error TS2715: Abstract property 'prop' in class 'AbstractClass' cannot be accessed in the constructor.
this.method(1);
// OK, references are to another instance
other.cb(other.prop);
yetAnother.cb(yetAnother.prop);
}
}
class Implementation extends DerivedAbstractClass {
prop = "";
cb = (s: string) => {};
constructor(str: string, other: AbstractClass, yetAnother: DerivedAbstractClass) {
super(str, other, yetAnother);
this.cb(this.prop);
}
method(n: number) {
this.cb(this.prop + n);
}
}
class User {
constructor(a: AbstractClass) {
a.prop;

View file

@ -31,6 +31,36 @@ abstract class AbstractClass {
}
}
abstract class DerivedAbstractClass extends AbstractClass {
cb = (s: string) => {};
constructor(str: string, other: AbstractClass, yetAnother: DerivedAbstractClass) {
super(str, other);
// there is no implementation of 'prop' in any base class
this.cb(this.prop.toLowerCase());
this.method(1);
// OK, references are to another instance
other.cb(other.prop);
yetAnother.cb(yetAnother.prop);
}
}
class Implementation extends DerivedAbstractClass {
prop = "";
cb = (s: string) => {};
constructor(str: string, other: AbstractClass, yetAnother: DerivedAbstractClass) {
super(str, other, yetAnother);
this.cb(this.prop);
}
method(n: number) {
this.cb(this.prop + n);
}
}
class User {
constructor(a: AbstractClass) {
a.prop;
@ -42,6 +72,19 @@ class User {
//// [abstractPropertyInConstructor.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 AbstractClass = /** @class */ (function () {
function AbstractClass(str, other) {
var _this = this;
@ -65,6 +108,35 @@ var AbstractClass = /** @class */ (function () {
};
return AbstractClass;
}());
var DerivedAbstractClass = /** @class */ (function (_super) {
__extends(DerivedAbstractClass, _super);
function DerivedAbstractClass(str, other, yetAnother) {
var _this = _super.call(this, str, other) || this;
_this.cb = function (s) { };
// there is no implementation of 'prop' in any base class
_this.cb(_this.prop.toLowerCase());
_this.method(1);
// OK, references are to another instance
other.cb(other.prop);
yetAnother.cb(yetAnother.prop);
return _this;
}
return DerivedAbstractClass;
}(AbstractClass));
var Implementation = /** @class */ (function (_super) {
__extends(Implementation, _super);
function Implementation(str, other, yetAnother) {
var _this = _super.call(this, str, other, yetAnother) || this;
_this.prop = "";
_this.cb = function (s) { };
_this.cb(_this.prop);
return _this;
}
Implementation.prototype.method = function (n) {
this.cb(this.prop + n);
};
return Implementation;
}(DerivedAbstractClass));
var User = /** @class */ (function () {
function User(a) {
a.prop;

View file

@ -92,31 +92,134 @@ abstract class AbstractClass {
}
}
abstract class DerivedAbstractClass extends AbstractClass {
>DerivedAbstractClass : Symbol(DerivedAbstractClass, Decl(abstractPropertyInConstructor.ts, 30, 1))
>AbstractClass : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
cb = (s: string) => {};
>cb : Symbol(DerivedAbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 32, 59))
>s : Symbol(s, Decl(abstractPropertyInConstructor.ts, 33, 10))
constructor(str: string, other: AbstractClass, yetAnother: DerivedAbstractClass) {
>str : Symbol(str, Decl(abstractPropertyInConstructor.ts, 35, 16))
>other : Symbol(other, Decl(abstractPropertyInConstructor.ts, 35, 28))
>AbstractClass : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
>yetAnother : Symbol(yetAnother, Decl(abstractPropertyInConstructor.ts, 35, 50))
>DerivedAbstractClass : Symbol(DerivedAbstractClass, Decl(abstractPropertyInConstructor.ts, 30, 1))
super(str, other);
>super : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
>str : Symbol(str, Decl(abstractPropertyInConstructor.ts, 35, 16))
>other : Symbol(other, Decl(abstractPropertyInConstructor.ts, 35, 28))
// there is no implementation of 'prop' in any base class
this.cb(this.prop.toLowerCase());
>this.cb : Symbol(DerivedAbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 32, 59))
>this : Symbol(DerivedAbstractClass, Decl(abstractPropertyInConstructor.ts, 30, 1))
>cb : Symbol(DerivedAbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 32, 59))
>this.prop.toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
>this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 17, 5))
>this : Symbol(DerivedAbstractClass, Decl(abstractPropertyInConstructor.ts, 30, 1))
>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 17, 5))
>toLowerCase : Symbol(String.toLowerCase, Decl(lib.es5.d.ts, --, --))
this.method(1);
>this.method : Symbol(AbstractClass.method, Decl(abstractPropertyInConstructor.ts, 20, 37))
>this : Symbol(DerivedAbstractClass, Decl(abstractPropertyInConstructor.ts, 30, 1))
>method : Symbol(AbstractClass.method, Decl(abstractPropertyInConstructor.ts, 20, 37))
// OK, references are to another instance
other.cb(other.prop);
>other.cb : Symbol(AbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 19, 26))
>other : Symbol(other, Decl(abstractPropertyInConstructor.ts, 35, 28))
>cb : Symbol(AbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 19, 26))
>other.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 17, 5))
>other : Symbol(other, Decl(abstractPropertyInConstructor.ts, 35, 28))
>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 17, 5))
yetAnother.cb(yetAnother.prop);
>yetAnother.cb : Symbol(DerivedAbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 32, 59))
>yetAnother : Symbol(yetAnother, Decl(abstractPropertyInConstructor.ts, 35, 50))
>cb : Symbol(DerivedAbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 32, 59))
>yetAnother.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 17, 5))
>yetAnother : Symbol(yetAnother, Decl(abstractPropertyInConstructor.ts, 35, 50))
>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 17, 5))
}
}
class Implementation extends DerivedAbstractClass {
>Implementation : Symbol(Implementation, Decl(abstractPropertyInConstructor.ts, 46, 1))
>DerivedAbstractClass : Symbol(DerivedAbstractClass, Decl(abstractPropertyInConstructor.ts, 30, 1))
prop = "";
>prop : Symbol(Implementation.prop, Decl(abstractPropertyInConstructor.ts, 48, 51))
cb = (s: string) => {};
>cb : Symbol(Implementation.cb, Decl(abstractPropertyInConstructor.ts, 49, 14))
>s : Symbol(s, Decl(abstractPropertyInConstructor.ts, 50, 10))
constructor(str: string, other: AbstractClass, yetAnother: DerivedAbstractClass) {
>str : Symbol(str, Decl(abstractPropertyInConstructor.ts, 52, 16))
>other : Symbol(other, Decl(abstractPropertyInConstructor.ts, 52, 28))
>AbstractClass : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
>yetAnother : Symbol(yetAnother, Decl(abstractPropertyInConstructor.ts, 52, 50))
>DerivedAbstractClass : Symbol(DerivedAbstractClass, Decl(abstractPropertyInConstructor.ts, 30, 1))
super(str, other, yetAnother);
>super : Symbol(DerivedAbstractClass, Decl(abstractPropertyInConstructor.ts, 30, 1))
>str : Symbol(str, Decl(abstractPropertyInConstructor.ts, 52, 16))
>other : Symbol(other, Decl(abstractPropertyInConstructor.ts, 52, 28))
>yetAnother : Symbol(yetAnother, Decl(abstractPropertyInConstructor.ts, 52, 50))
this.cb(this.prop);
>this.cb : Symbol(Implementation.cb, Decl(abstractPropertyInConstructor.ts, 49, 14))
>this : Symbol(Implementation, Decl(abstractPropertyInConstructor.ts, 46, 1))
>cb : Symbol(Implementation.cb, Decl(abstractPropertyInConstructor.ts, 49, 14))
>this.prop : Symbol(Implementation.prop, Decl(abstractPropertyInConstructor.ts, 48, 51))
>this : Symbol(Implementation, Decl(abstractPropertyInConstructor.ts, 46, 1))
>prop : Symbol(Implementation.prop, Decl(abstractPropertyInConstructor.ts, 48, 51))
}
method(n: number) {
>method : Symbol(Implementation.method, Decl(abstractPropertyInConstructor.ts, 55, 5))
>n : Symbol(n, Decl(abstractPropertyInConstructor.ts, 57, 11))
this.cb(this.prop + n);
>this.cb : Symbol(Implementation.cb, Decl(abstractPropertyInConstructor.ts, 49, 14))
>this : Symbol(Implementation, Decl(abstractPropertyInConstructor.ts, 46, 1))
>cb : Symbol(Implementation.cb, Decl(abstractPropertyInConstructor.ts, 49, 14))
>this.prop : Symbol(Implementation.prop, Decl(abstractPropertyInConstructor.ts, 48, 51))
>this : Symbol(Implementation, Decl(abstractPropertyInConstructor.ts, 46, 1))
>prop : Symbol(Implementation.prop, Decl(abstractPropertyInConstructor.ts, 48, 51))
>n : Symbol(n, Decl(abstractPropertyInConstructor.ts, 57, 11))
}
}
class User {
>User : Symbol(User, Decl(abstractPropertyInConstructor.ts, 30, 1))
>User : Symbol(User, Decl(abstractPropertyInConstructor.ts, 60, 1))
constructor(a: AbstractClass) {
>a : Symbol(a, Decl(abstractPropertyInConstructor.ts, 33, 16))
>a : Symbol(a, Decl(abstractPropertyInConstructor.ts, 63, 16))
>AbstractClass : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
a.prop;
>a.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 17, 5))
>a : Symbol(a, Decl(abstractPropertyInConstructor.ts, 33, 16))
>a : Symbol(a, Decl(abstractPropertyInConstructor.ts, 63, 16))
>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 17, 5))
a.cb("hi");
>a.cb : Symbol(AbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 19, 26))
>a : Symbol(a, Decl(abstractPropertyInConstructor.ts, 33, 16))
>a : Symbol(a, Decl(abstractPropertyInConstructor.ts, 63, 16))
>cb : Symbol(AbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 19, 26))
a.method(12);
>a.method : Symbol(AbstractClass.method, Decl(abstractPropertyInConstructor.ts, 20, 37))
>a : Symbol(a, Decl(abstractPropertyInConstructor.ts, 33, 16))
>a : Symbol(a, Decl(abstractPropertyInConstructor.ts, 63, 16))
>method : Symbol(AbstractClass.method, Decl(abstractPropertyInConstructor.ts, 20, 37))
a.method2();
>a.method2 : Symbol(AbstractClass.method2, Decl(abstractPropertyInConstructor.ts, 25, 25))
>a : Symbol(a, Decl(abstractPropertyInConstructor.ts, 33, 16))
>a : Symbol(a, Decl(abstractPropertyInConstructor.ts, 63, 16))
>method2 : Symbol(AbstractClass.method2, Decl(abstractPropertyInConstructor.ts, 25, 25))
}
}

View file

@ -104,6 +104,119 @@ abstract class AbstractClass {
}
}
abstract class DerivedAbstractClass extends AbstractClass {
>DerivedAbstractClass : DerivedAbstractClass
>AbstractClass : AbstractClass
cb = (s: string) => {};
>cb : (s: string) => void
>(s: string) => {} : (s: string) => void
>s : string
constructor(str: string, other: AbstractClass, yetAnother: DerivedAbstractClass) {
>str : string
>other : AbstractClass
>yetAnother : DerivedAbstractClass
super(str, other);
>super(str, other) : void
>super : typeof AbstractClass
>str : string
>other : AbstractClass
// there is no implementation of 'prop' in any base class
this.cb(this.prop.toLowerCase());
>this.cb(this.prop.toLowerCase()) : void
>this.cb : (s: string) => void
>this : this
>cb : (s: string) => void
>this.prop.toLowerCase() : string
>this.prop.toLowerCase : () => string
>this.prop : string
>this : this
>prop : string
>toLowerCase : () => string
this.method(1);
>this.method(1) : void
>this.method : (num: number) => void
>this : this
>method : (num: number) => void
>1 : 1
// OK, references are to another instance
other.cb(other.prop);
>other.cb(other.prop) : void
>other.cb : (s: string) => void
>other : AbstractClass
>cb : (s: string) => void
>other.prop : string
>other : AbstractClass
>prop : string
yetAnother.cb(yetAnother.prop);
>yetAnother.cb(yetAnother.prop) : void
>yetAnother.cb : (s: string) => void
>yetAnother : DerivedAbstractClass
>cb : (s: string) => void
>yetAnother.prop : string
>yetAnother : DerivedAbstractClass
>prop : string
}
}
class Implementation extends DerivedAbstractClass {
>Implementation : Implementation
>DerivedAbstractClass : DerivedAbstractClass
prop = "";
>prop : string
>"" : ""
cb = (s: string) => {};
>cb : (s: string) => void
>(s: string) => {} : (s: string) => void
>s : string
constructor(str: string, other: AbstractClass, yetAnother: DerivedAbstractClass) {
>str : string
>other : AbstractClass
>yetAnother : DerivedAbstractClass
super(str, other, yetAnother);
>super(str, other, yetAnother) : void
>super : typeof DerivedAbstractClass
>str : string
>other : AbstractClass
>yetAnother : DerivedAbstractClass
this.cb(this.prop);
>this.cb(this.prop) : void
>this.cb : (s: string) => void
>this : this
>cb : (s: string) => void
>this.prop : string
>this : this
>prop : string
}
method(n: number) {
>method : (n: number) => void
>n : number
this.cb(this.prop + n);
>this.cb(this.prop + n) : void
>this.cb : (s: string) => void
>this : this
>cb : (s: string) => void
>this.prop + n : string
>this.prop : string
>this : this
>prop : string
>n : number
}
}
class User {
>User : User

View file

@ -30,6 +30,36 @@ abstract class AbstractClass {
}
}
abstract class DerivedAbstractClass extends AbstractClass {
cb = (s: string) => {};
constructor(str: string, other: AbstractClass, yetAnother: DerivedAbstractClass) {
super(str, other);
// there is no implementation of 'prop' in any base class
this.cb(this.prop.toLowerCase());
this.method(1);
// OK, references are to another instance
other.cb(other.prop);
yetAnother.cb(yetAnother.prop);
}
}
class Implementation extends DerivedAbstractClass {
prop = "";
cb = (s: string) => {};
constructor(str: string, other: AbstractClass, yetAnother: DerivedAbstractClass) {
super(str, other, yetAnother);
this.cb(this.prop);
}
method(n: number) {
this.cb(this.prop + n);
}
}
class User {
constructor(a: AbstractClass) {
a.prop;