Error when accessing abstract property in constructor #9230

This commit is contained in:
Charles Pierce 2017-10-06 19:46:29 -07:00
parent e821c2b6e9
commit 9e00df590d
7 changed files with 201 additions and 5 deletions

View file

@ -14826,11 +14826,7 @@ namespace ts {
// where this references the constructor function object of a derived class,
// a super property access is permitted and must specify a public static member function of the base class.
if (languageVersion < ScriptTarget.ES2015) {
const hasNonMethodDeclaration = forEachProperty(prop, p => {
const propKind = getDeclarationKindFromSymbol(p);
return propKind !== SyntaxKind.MethodDeclaration && propKind !== SyntaxKind.MethodSignature;
});
if (hasNonMethodDeclaration) {
if (symbolHasNonMethodDeclaration(prop)) {
error(errorNode, Diagnostics.Only_public_and_protected_methods_of_the_base_class_are_accessible_via_the_super_keyword);
return false;
}
@ -14845,6 +14841,17 @@ namespace ts {
}
}
// Referencing Abstract Properties within Constructors is not allowed
if ((flags & ModifierFlags.Abstract) && symbolHasNonMethodDeclaration(prop)) {
const declaringClassDeclaration = <ClassLikeDeclaration>getClassLikeDeclarationOfSymbol(getParentOfSymbol(prop));
const declaringClassConstructor = declaringClassDeclaration && findConstructorDeclaration(declaringClassDeclaration);
if (declaringClassConstructor && isNodeWithinFunction(node, declaringClassConstructor)) {
error(errorNode, Diagnostics.Abstract_property_0_in_class_1_cannot_be_accessed_in_constructor, symbolToString(prop), typeToString(getDeclaringClass(prop)));
return false;
}
}
// Public properties are otherwise accessible.
if (!(flags & ModifierFlags.NonPublicAccessibilityModifier)) {
return true;
@ -14896,6 +14903,13 @@ namespace ts {
return true;
}
function symbolHasNonMethodDeclaration(symbol: Symbol) {
return forEachProperty(symbol, prop => {
const propKind = getDeclarationKindFromSymbol(prop);
return propKind !== SyntaxKind.MethodDeclaration && propKind !== SyntaxKind.MethodSignature;
});
}
function checkNonNullExpression(node: Expression | QualifiedName) {
return checkNonNullType(checkExpression(node), node);
}
@ -23139,6 +23153,10 @@ namespace ts {
return result;
}
function isNodeWithinFunction(node: Node, functionDeclaration: FunctionLike) {
return getContainingFunction(node) === functionDeclaration;
}
function isNodeWithinClass(node: Node, classDeclaration: ClassLikeDeclaration) {
return !!forEachEnclosingClass(node, n => n === classDeclaration);
}

View file

@ -2220,6 +2220,10 @@
"category": "Error",
"code": 2714
},
"Abstract property '{0}' in class '{1}' cannot be accessed in constructor.": {
"category": "Error",
"code": 2715
},
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",

View file

@ -0,0 +1,25 @@
tests/cases/compiler/abstractPropertyInConstructor.ts(4,24): error TS2715: Abstract property 'prop' in class 'AbstractClass' cannot be accessed in constructor.
tests/cases/compiler/abstractPropertyInConstructor.ts(5,14): error TS2715: Abstract property 'prop' in class 'AbstractClass' cannot be accessed in constructor.
==== tests/cases/compiler/abstractPropertyInConstructor.ts (2 errors) ====
abstract class AbstractClass {
constructor(str: string) {
this.method(parseInt(str));
let val = this.prop.toLowerCase();
~~~~
!!! error TS2715: Abstract property 'prop' in class 'AbstractClass' cannot be accessed in constructor.
this.prop = "Hello World";
~~~~
!!! error TS2715: Abstract property 'prop' in class 'AbstractClass' cannot be accessed in constructor.
}
abstract prop: string;
abstract method(num: number): void;
method2() {
this.prop = this.prop + "!";
}
}

View file

@ -0,0 +1,30 @@
//// [abstractPropertyInConstructor.ts]
abstract class AbstractClass {
constructor(str: string) {
this.method(parseInt(str));
let val = this.prop.toLowerCase();
this.prop = "Hello World";
}
abstract prop: string;
abstract method(num: number): void;
method2() {
this.prop = this.prop + "!";
}
}
//// [abstractPropertyInConstructor.js]
var AbstractClass = /** @class */ (function () {
function AbstractClass(str) {
this.method(parseInt(str));
var val = this.prop.toLowerCase();
this.prop = "Hello World";
}
AbstractClass.prototype.method2 = function () {
this.prop = this.prop + "!";
};
return AbstractClass;
}());

View file

@ -0,0 +1,48 @@
=== tests/cases/compiler/abstractPropertyInConstructor.ts ===
abstract class AbstractClass {
>AbstractClass : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
constructor(str: string) {
>str : Symbol(str, Decl(abstractPropertyInConstructor.ts, 1, 16))
this.method(parseInt(str));
>this.method : Symbol(AbstractClass.method, Decl(abstractPropertyInConstructor.ts, 7, 26))
>this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
>method : Symbol(AbstractClass.method, Decl(abstractPropertyInConstructor.ts, 7, 26))
>parseInt : Symbol(parseInt, Decl(lib.d.ts, --, --))
>str : Symbol(str, Decl(abstractPropertyInConstructor.ts, 1, 16))
let val = this.prop.toLowerCase();
>val : Symbol(val, Decl(abstractPropertyInConstructor.ts, 3, 11))
>this.prop.toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, --, --))
>this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 5, 5))
>this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 5, 5))
>toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, --, --))
this.prop = "Hello World";
>this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 5, 5))
>this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 5, 5))
}
abstract prop: string;
>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 5, 5))
abstract method(num: number): void;
>method : Symbol(AbstractClass.method, Decl(abstractPropertyInConstructor.ts, 7, 26))
>num : Symbol(num, Decl(abstractPropertyInConstructor.ts, 9, 20))
method2() {
>method2 : Symbol(AbstractClass.method2, Decl(abstractPropertyInConstructor.ts, 9, 39))
this.prop = this.prop + "!";
>this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 5, 5))
>this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 5, 5))
>this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 5, 5))
>this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 5, 5))
}
}

View file

@ -0,0 +1,56 @@
=== tests/cases/compiler/abstractPropertyInConstructor.ts ===
abstract class AbstractClass {
>AbstractClass : AbstractClass
constructor(str: string) {
>str : string
this.method(parseInt(str));
>this.method(parseInt(str)) : void
>this.method : (num: number) => void
>this : this
>method : (num: number) => void
>parseInt(str) : number
>parseInt : (s: string, radix?: number) => number
>str : string
let val = this.prop.toLowerCase();
>val : string
>this.prop.toLowerCase() : string
>this.prop.toLowerCase : () => string
>this.prop : string
>this : this
>prop : string
>toLowerCase : () => string
this.prop = "Hello World";
>this.prop = "Hello World" : "Hello World"
>this.prop : string
>this : this
>prop : string
>"Hello World" : "Hello World"
}
abstract prop: string;
>prop : string
abstract method(num: number): void;
>method : (num: number) => void
>num : number
method2() {
>method2 : () => void
this.prop = this.prop + "!";
>this.prop = this.prop + "!" : string
>this.prop : string
>this : this
>prop : string
>this.prop + "!" : string
>this.prop : string
>this : this
>prop : string
>"!" : "!"
}
}

View file

@ -0,0 +1,15 @@
abstract class AbstractClass {
constructor(str: string) {
this.method(parseInt(str));
let val = this.prop.toLowerCase();
this.prop = "Hello World";
}
abstract prop: string;
abstract method(num: number): void;
method2() {
this.prop = this.prop + "!";
}
}