Error when accessing abstract property in constructor #9230
This commit is contained in:
parent
e821c2b6e9
commit
9e00df590d
7 changed files with 201 additions and 5 deletions
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 + "!";
|
||||
}
|
||||
}
|
||||
|
30
tests/baselines/reference/abstractPropertyInConstructor.js
Normal file
30
tests/baselines/reference/abstractPropertyInConstructor.js
Normal 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;
|
||||
}());
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
>"!" : "!"
|
||||
}
|
||||
}
|
||||
|
15
tests/cases/compiler/abstractPropertyInConstructor.ts
Normal file
15
tests/cases/compiler/abstractPropertyInConstructor.ts
Normal 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 + "!";
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue