diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9a483ae50ed..8afee9455cf 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14844,10 +14844,9 @@ namespace ts { // Referencing Abstract Properties within Constructors is not allowed if ((flags & ModifierFlags.Abstract) && symbolHasNonMethodDeclaration(prop)) { const declaringClassDeclaration = 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))); + 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; } } @@ -23153,8 +23152,16 @@ namespace ts { return result; } - function isNodeWithinFunction(node: Node, functionDeclaration: FunctionLike) { - return getContainingFunction(node) === functionDeclaration; + 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) { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index e2d514ba268..9b220a880b0 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2220,7 +2220,7 @@ "category": "Error", "code": 2714 }, - "Abstract property '{0}' in class '{1}' cannot be accessed in constructor.": { + "Abstract property '{0}' in class '{1}' cannot be accessed in the constructor.": { "category": "Error", "code": 2715 }, diff --git a/tests/baselines/reference/abstractPropertyInConstructor.errors.txt b/tests/baselines/reference/abstractPropertyInConstructor.errors.txt index 7e654b440c6..461dd713d3b 100644 --- a/tests/baselines/reference/abstractPropertyInConstructor.errors.txt +++ b/tests/baselines/reference/abstractPropertyInConstructor.errors.txt @@ -1,20 +1,32 @@ -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(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 (2 errors) ==== +==== 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 constructor. - this.prop = "Hello World"; - ~~~~ -!!! error TS2715: Abstract property 'prop' in class 'AbstractClass' cannot be accessed in constructor. +!!! 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; diff --git a/tests/baselines/reference/abstractPropertyInConstructor.js b/tests/baselines/reference/abstractPropertyInConstructor.js index c6d7de6c037..18a2937a191 100644 --- a/tests/baselines/reference/abstractPropertyInConstructor.js +++ b/tests/baselines/reference/abstractPropertyInConstructor.js @@ -3,10 +3,19 @@ abstract class AbstractClass { constructor(str: string) { this.method(parseInt(str)); let val = this.prop.toLowerCase(); - this.prop = "Hello World"; + + 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; @@ -19,9 +28,16 @@ abstract class AbstractClass { //// [abstractPropertyInConstructor.js] var AbstractClass = /** @class */ (function () { function AbstractClass(str) { + var _this = this; this.method(parseInt(str)); var val = this.prop.toLowerCase(); - this.prop = "Hello World"; + if (!str) { + this.prop = "Hello World"; + } + this.cb(str); + var innerFunction = function () { + return _this.prop; + }; } AbstractClass.prototype.method2 = function () { this.prop = this.prop + "!"; diff --git a/tests/baselines/reference/abstractPropertyInConstructor.symbols b/tests/baselines/reference/abstractPropertyInConstructor.symbols index 7d634f80267..0d542ffb0a8 100644 --- a/tests/baselines/reference/abstractPropertyInConstructor.symbols +++ b/tests/baselines/reference/abstractPropertyInConstructor.symbols @@ -6,43 +6,65 @@ abstract class AbstractClass { >str : Symbol(str, Decl(abstractPropertyInConstructor.ts, 1, 16)) this.method(parseInt(str)); ->this.method : Symbol(AbstractClass.method, Decl(abstractPropertyInConstructor.ts, 7, 26)) +>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, 7, 26)) +>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, 5, 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, 5, 5)) +>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5)) >toLowerCase : Symbol(String.toLowerCase, Decl(lib.d.ts, --, --)) - this.prop = "Hello World"; ->this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 5, 5)) + 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, 5, 5)) +>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, 5, 5)) +>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, 7, 26)) ->num : Symbol(num, Decl(abstractPropertyInConstructor.ts, 9, 20)) +>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, 9, 39)) +>method2 : Symbol(AbstractClass.method2, Decl(abstractPropertyInConstructor.ts, 18, 39)) this.prop = this.prop + "!"; ->this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 5, 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, 5, 5)) ->this.prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 5, 5)) +>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, 5, 5)) +>prop : Symbol(AbstractClass.prop, Decl(abstractPropertyInConstructor.ts, 13, 5)) } } diff --git a/tests/baselines/reference/abstractPropertyInConstructor.types b/tests/baselines/reference/abstractPropertyInConstructor.types index 05f7a7752e6..0ffb5f1bdfd 100644 --- a/tests/baselines/reference/abstractPropertyInConstructor.types +++ b/tests/baselines/reference/abstractPropertyInConstructor.types @@ -23,17 +23,42 @@ abstract class AbstractClass { >prop : string >toLowerCase : () => string - this.prop = "Hello World"; + 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 diff --git a/tests/cases/compiler/abstractPropertyInConstructor.ts b/tests/cases/compiler/abstractPropertyInConstructor.ts index 5376aae9d6f..457fdb473b1 100644 --- a/tests/cases/compiler/abstractPropertyInConstructor.ts +++ b/tests/cases/compiler/abstractPropertyInConstructor.ts @@ -2,10 +2,19 @@ abstract class AbstractClass { constructor(str: string) { this.method(parseInt(str)); let val = this.prop.toLowerCase(); - this.prop = "Hello World"; + + 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;