Merge pull request #19005 from charlespierce/abstract_property_in_constructor

Error when accessing abstract property in constructor #9230
This commit is contained in:
Nathan Shively-Sanders
2017-10-09 13:21:33 -07:00
committed by GitHub
7 changed files with 293 additions and 5 deletions

View File

@@ -14830,11 +14830,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;
}
@@ -14849,6 +14845,16 @@ namespace ts {
}
}
// Referencing Abstract Properties within Constructors is not allowed
if ((flags & ModifierFlags.Abstract) && symbolHasNonMethodDeclaration(prop)) {
const declaringClassDeclaration = <ClassLikeDeclaration>getClassLikeDeclarationOfSymbol(getParentOfSymbol(prop));
if (declaringClassDeclaration && isNodeWithinConstructor(node, declaringClassDeclaration)) {
error(errorNode, Diagnostics.Abstract_property_0_in_class_1_cannot_be_accessed_in_the_constructor, symbolToString(prop), typeToString(getDeclaringClass(prop)));
return false;
}
}
// Public properties are otherwise accessible.
if (!(flags & ModifierFlags.NonPublicAccessibilityModifier)) {
return true;
@@ -14900,6 +14906,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);
}
@@ -23143,6 +23156,19 @@ namespace ts {
return result;
}
function isNodeWithinConstructor(node: Node, classDeclaration: ClassLikeDeclaration) {
return findAncestor(node, element => {
if (isConstructorDeclaration(element) && nodeIsPresent(element.body)) {
return true;
}
else if (element === classDeclaration || isFunctionLikeDeclaration(element)) {
return "quit";
}
return false;
});
}
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 the constructor.": {
"category": "Error",
"code": 2715
},
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",

View File

@@ -0,0 +1,37 @@
tests/cases/compiler/abstractPropertyInConstructor.ts(4,24): error TS2715: Abstract property 'prop' in class 'AbstractClass' cannot be accessed in the constructor.
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 (3 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 the constructor.
if (!str) {
this.prop = "Hello World";
~~~~
!!! error TS2715: Abstract property 'prop' in class 'AbstractClass' cannot be accessed in the constructor.
}
this.cb(str);
~~
!!! error TS2715: Abstract property 'cb' in class 'AbstractClass' cannot be accessed in the constructor.
const innerFunction = () => {
return this.prop;
}
}
abstract prop: string;
abstract cb: (s: string) => void;
abstract method(num: number): void;
method2() {
this.prop = this.prop + "!";
}
}

View File

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

View File

@@ -0,0 +1,70 @@
=== 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, 16, 37))
>this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
>method : Symbol(AbstractClass.method, Decl(abstractPropertyInConstructor.ts, 16, 37))
>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, 13, 5))
>this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5))
>toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, --, --))
if (!str) {
>str : Symbol(str, Decl(abstractPropertyInConstructor.ts, 1, 16))
this.prop = "Hello World";
>this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5))
>this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5))
}
this.cb(str);
>this.cb : Symbol(AbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 15, 26))
>this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
>cb : Symbol(AbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 15, 26))
>str : Symbol(str, Decl(abstractPropertyInConstructor.ts, 1, 16))
const innerFunction = () => {
>innerFunction : Symbol(innerFunction, Decl(abstractPropertyInConstructor.ts, 10, 13))
return this.prop;
>this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5))
>this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5))
}
}
abstract prop: string;
>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5))
abstract cb: (s: string) => void;
>cb : Symbol(AbstractClass.cb, Decl(abstractPropertyInConstructor.ts, 15, 26))
>s : Symbol(s, Decl(abstractPropertyInConstructor.ts, 16, 18))
abstract method(num: number): void;
>method : Symbol(AbstractClass.method, Decl(abstractPropertyInConstructor.ts, 16, 37))
>num : Symbol(num, Decl(abstractPropertyInConstructor.ts, 18, 20))
method2() {
>method2 : Symbol(AbstractClass.method2, Decl(abstractPropertyInConstructor.ts, 18, 39))
this.prop = this.prop + "!";
>this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5))
>this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5))
>this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5))
>this : Symbol(AbstractClass, Decl(abstractPropertyInConstructor.ts, 0, 0))
>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5))
}
}

View File

@@ -0,0 +1,81 @@
=== 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
if (!str) {
>!str : boolean
>str : string
this.prop = "Hello World";
>this.prop = "Hello World" : "Hello World"
>this.prop : string
>this : this
>prop : string
>"Hello World" : "Hello World"
}
this.cb(str);
>this.cb(str) : void
>this.cb : (s: string) => void
>this : this
>cb : (s: string) => void
>str : string
const innerFunction = () => {
>innerFunction : () => string
>() => { return this.prop; } : () => string
return this.prop;
>this.prop : string
>this : this
>prop : string
}
}
abstract prop: string;
>prop : string
abstract cb: (s: string) => void;
>cb : (s: string) => void
>s : 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,24 @@
abstract class AbstractClass {
constructor(str: string) {
this.method(parseInt(str));
let val = this.prop.toLowerCase();
if (!str) {
this.prop = "Hello World";
}
this.cb(str);
const innerFunction = () => {
return this.prop;
}
}
abstract prop: string;
abstract cb: (s: string) => void;
abstract method(num: number): void;
method2() {
this.prop = this.prop + "!";
}
}