Skip to content

Commit b1f86ec

Browse files
authored
fix(42265): Error accessing abstract property in constructor via destructuring (microsoft#42276)
1 parent 2e49e28 commit b1f86ec

9 files changed

+270
-34
lines changed

src/compiler/checker.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -26443,7 +26443,9 @@ namespace ts {
2644326443
node: PropertyAccessExpression | QualifiedName | PropertyAccessExpression | VariableDeclaration | ParameterDeclaration | ImportTypeNode | PropertyAssignment | ShorthandPropertyAssignment | BindingElement,
2644426444
isSuper: boolean, type: Type, prop: Symbol): boolean {
2644526445
const flags = getDeclarationModifierFlagsFromSymbol(prop);
26446-
const errorNode = node.kind === SyntaxKind.QualifiedName ? node.right : node.kind === SyntaxKind.ImportType ? node : node.name;
26446+
const errorNode = node.kind === SyntaxKind.QualifiedName ? node.right :
26447+
node.kind === SyntaxKind.ImportType ? node :
26448+
node.kind === SyntaxKind.BindingElement && node.propertyName ? node.propertyName : node.name;
2644726449

2644826450
if (isSuper) {
2644926451
// TS 1.0 spec (April 2014): 4.8.2
@@ -26470,7 +26472,8 @@ namespace ts {
2647026472
}
2647126473

2647226474
// Referencing abstract properties within their own constructors is not allowed
26473-
if ((flags & ModifierFlags.Abstract) && isThisProperty(node) && symbolHasNonMethodDeclaration(prop)) {
26475+
if ((flags & ModifierFlags.Abstract) && symbolHasNonMethodDeclaration(prop) &&
26476+
(isThisProperty(node) || isThisInitializedObjectBindingExpression(node) || isObjectBindingPattern(node.parent) && isThisInitializedDeclaration(node.parent.parent))) {
2647426477
const declaringClassDeclaration = getClassLikeDeclarationOfSymbol(getParentOfSymbol(prop)!);
2647526478
if (declaringClassDeclaration && isNodeUsedDuringClassInitialization(node)) {
2647626479
error(errorNode, Diagnostics.Abstract_property_0_in_class_1_cannot_be_accessed_in_the_constructor, symbolToString(prop), getTextOfIdentifierOrLiteral(declaringClassDeclaration.name!)); // TODO: GH#18217
@@ -34698,7 +34701,7 @@ namespace ts {
3469834701
const property = getPropertyOfType(parentType, nameText);
3469934702
if (property) {
3470034703
markPropertyAsReferenced(property, /*nodeForCheckWriteOnly*/ undefined, /*isThisAccess*/ false); // A destructuring is never a write-only reference.
34701-
checkPropertyAccessibility(parent, !!parent.initializer && parent.initializer.kind === SyntaxKind.SuperKeyword, parentType, property);
34704+
checkPropertyAccessibility(node, !!parent.initializer && parent.initializer.kind === SyntaxKind.SuperKeyword, parentType, property);
3470234705
}
3470334706
}
3470434707
}

src/compiler/utilities.ts

+8
Original file line numberDiff line numberDiff line change
@@ -1731,6 +1731,14 @@ namespace ts {
17311731
return !!node && isVariableDeclaration(node) && node.initializer?.kind === SyntaxKind.ThisKeyword;
17321732
}
17331733

1734+
export function isThisInitializedObjectBindingExpression(node: Node | undefined): boolean {
1735+
return !!node
1736+
&& (isShorthandPropertyAssignment(node) || isPropertyAssignment(node))
1737+
&& isBinaryExpression(node.parent.parent)
1738+
&& node.parent.parent.operatorToken.kind === SyntaxKind.EqualsToken
1739+
&& node.parent.parent.right.kind === SyntaxKind.ThisKeyword;
1740+
}
1741+
17341742
export function getEntityNameFromTypeNode(node: TypeNode): EntityNameOrEntityNameExpression | undefined {
17351743
switch (node.kind) {
17361744
case SyntaxKind.TypeReference:

tests/baselines/reference/abstractPropertyInConstructor.errors.txt

+38-1
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,14 @@ tests/cases/compiler/abstractPropertyInConstructor.ts(9,14): error TS2715: Abstr
44
tests/cases/compiler/abstractPropertyInConstructor.ts(25,18): error TS2715: Abstract property 'prop' in class 'AbstractClass' cannot be accessed in the constructor.
55
tests/cases/compiler/abstractPropertyInConstructor.ts(25,18): error TS2729: Property 'prop' is used before its initialization.
66
tests/cases/compiler/abstractPropertyInConstructor.ts(39,22): error TS2715: Abstract property 'prop' in class 'AbstractClass' cannot be accessed in the constructor.
7+
tests/cases/compiler/abstractPropertyInConstructor.ts(78,15): error TS2715: Abstract property 'x' in class 'C1' cannot be accessed in the constructor.
8+
tests/cases/compiler/abstractPropertyInConstructor.ts(78,18): error TS2715: Abstract property 'y' in class 'C1' cannot be accessed in the constructor.
9+
tests/cases/compiler/abstractPropertyInConstructor.ts(79,12): error TS2715: Abstract property 'x' in class 'C1' cannot be accessed in the constructor.
10+
tests/cases/compiler/abstractPropertyInConstructor.ts(79,15): error TS2715: Abstract property 'y' in class 'C1' cannot be accessed in the constructor.
11+
tests/cases/compiler/abstractPropertyInConstructor.ts(79,22): error TS2715: Abstract property 'y' in class 'C1' cannot be accessed in the constructor.
712

813

9-
==== tests/cases/compiler/abstractPropertyInConstructor.ts (6 errors) ====
14+
==== tests/cases/compiler/abstractPropertyInConstructor.ts (11 errors) ====
1015
abstract class AbstractClass {
1116
constructor(str: string, other: AbstractClass) {
1217
this.method(parseInt(str));
@@ -90,4 +95,36 @@ tests/cases/compiler/abstractPropertyInConstructor.ts(39,22): error TS2715: Abst
9095
a.method2();
9196
}
9297
}
98+
99+
abstract class C1 {
100+
abstract x: string;
101+
abstract y: string;
102+
103+
constructor() {
104+
let self = this; // ok
105+
let { x, y: y1 } = this; // error
106+
~
107+
!!! error TS2715: Abstract property 'x' in class 'C1' cannot be accessed in the constructor.
108+
~
109+
!!! error TS2715: Abstract property 'y' in class 'C1' cannot be accessed in the constructor.
110+
({ x, y: y1, "y": y1 } = this); // error
111+
~
112+
!!! error TS2715: Abstract property 'x' in class 'C1' cannot be accessed in the constructor.
113+
~
114+
!!! error TS2715: Abstract property 'y' in class 'C1' cannot be accessed in the constructor.
115+
~~~
116+
!!! error TS2715: Abstract property 'y' in class 'C1' cannot be accessed in the constructor.
117+
}
118+
}
119+
120+
class C2 {
121+
x: string;
122+
y: string;
123+
124+
constructor() {
125+
let self = this; // ok
126+
let { x, y: y1 } = this; // ok
127+
({ x, y: y1, "y": y1 } = this); // ok
128+
}
129+
}
93130

tests/baselines/reference/abstractPropertyInConstructor.js

+40
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,28 @@ class User {
6969
a.method2();
7070
}
7171
}
72+
73+
abstract class C1 {
74+
abstract x: string;
75+
abstract y: string;
76+
77+
constructor() {
78+
let self = this; // ok
79+
let { x, y: y1 } = this; // error
80+
({ x, y: y1, "y": y1 } = this); // error
81+
}
82+
}
83+
84+
class C2 {
85+
x: string;
86+
y: string;
87+
88+
constructor() {
89+
let self = this; // ok
90+
let { x, y: y1 } = this; // ok
91+
({ x, y: y1, "y": y1 } = this); // ok
92+
}
93+
}
7294

7395

7496
//// [abstractPropertyInConstructor.js]
@@ -148,3 +170,21 @@ var User = /** @class */ (function () {
148170
}
149171
return User;
150172
}());
173+
var C1 = /** @class */ (function () {
174+
function C1() {
175+
var _a;
176+
var self = this; // ok
177+
var _b = this, x = _b.x, y1 = _b.y; // error
178+
(_a = this, x = _a.x, y1 = _a.y, y1 = _a["y"]); // error
179+
}
180+
return C1;
181+
}());
182+
var C2 = /** @class */ (function () {
183+
function C2() {
184+
var _a;
185+
var self = this; // ok
186+
var _b = this, x = _b.x, y1 = _b.y; // ok
187+
(_a = this, x = _a.x, y1 = _a.y, y1 = _a["y"]); // ok
188+
}
189+
return C2;
190+
}());

tests/baselines/reference/abstractPropertyInConstructor.symbols

+60
Original file line numberDiff line numberDiff line change
@@ -224,3 +224,63 @@ class User {
224224
}
225225
}
226226

227+
abstract class C1 {
228+
>C1 : Symbol(C1, Decl(abstractPropertyInConstructor.ts, 69, 1))
229+
230+
abstract x: string;
231+
>x : Symbol(C1.x, Decl(abstractPropertyInConstructor.ts, 71, 19))
232+
233+
abstract y: string;
234+
>y : Symbol(C1.y, Decl(abstractPropertyInConstructor.ts, 72, 23))
235+
236+
constructor() {
237+
let self = this; // ok
238+
>self : Symbol(self, Decl(abstractPropertyInConstructor.ts, 76, 11))
239+
>this : Symbol(C1, Decl(abstractPropertyInConstructor.ts, 69, 1))
240+
241+
let { x, y: y1 } = this; // error
242+
>x : Symbol(x, Decl(abstractPropertyInConstructor.ts, 77, 13))
243+
>y : Symbol(C1.y, Decl(abstractPropertyInConstructor.ts, 72, 23))
244+
>y1 : Symbol(y1, Decl(abstractPropertyInConstructor.ts, 77, 16))
245+
>this : Symbol(C1, Decl(abstractPropertyInConstructor.ts, 69, 1))
246+
247+
({ x, y: y1, "y": y1 } = this); // error
248+
>x : Symbol(x, Decl(abstractPropertyInConstructor.ts, 78, 10))
249+
>y : Symbol(y, Decl(abstractPropertyInConstructor.ts, 78, 13), Decl(abstractPropertyInConstructor.ts, 78, 20))
250+
>y1 : Symbol(y1, Decl(abstractPropertyInConstructor.ts, 77, 16))
251+
>"y" : Symbol(y, Decl(abstractPropertyInConstructor.ts, 78, 13), Decl(abstractPropertyInConstructor.ts, 78, 20))
252+
>y1 : Symbol(y1, Decl(abstractPropertyInConstructor.ts, 77, 16))
253+
>this : Symbol(C1, Decl(abstractPropertyInConstructor.ts, 69, 1))
254+
}
255+
}
256+
257+
class C2 {
258+
>C2 : Symbol(C2, Decl(abstractPropertyInConstructor.ts, 80, 1))
259+
260+
x: string;
261+
>x : Symbol(C2.x, Decl(abstractPropertyInConstructor.ts, 82, 10))
262+
263+
y: string;
264+
>y : Symbol(C2.y, Decl(abstractPropertyInConstructor.ts, 83, 14))
265+
266+
constructor() {
267+
let self = this; // ok
268+
>self : Symbol(self, Decl(abstractPropertyInConstructor.ts, 87, 11))
269+
>this : Symbol(C2, Decl(abstractPropertyInConstructor.ts, 80, 1))
270+
271+
let { x, y: y1 } = this; // ok
272+
>x : Symbol(x, Decl(abstractPropertyInConstructor.ts, 88, 13))
273+
>y : Symbol(C2.y, Decl(abstractPropertyInConstructor.ts, 83, 14))
274+
>y1 : Symbol(y1, Decl(abstractPropertyInConstructor.ts, 88, 16))
275+
>this : Symbol(C2, Decl(abstractPropertyInConstructor.ts, 80, 1))
276+
277+
({ x, y: y1, "y": y1 } = this); // ok
278+
>x : Symbol(x, Decl(abstractPropertyInConstructor.ts, 89, 10))
279+
>y : Symbol(y, Decl(abstractPropertyInConstructor.ts, 89, 13), Decl(abstractPropertyInConstructor.ts, 89, 20))
280+
>y1 : Symbol(y1, Decl(abstractPropertyInConstructor.ts, 88, 16))
281+
>"y" : Symbol(y, Decl(abstractPropertyInConstructor.ts, 89, 13), Decl(abstractPropertyInConstructor.ts, 89, 20))
282+
>y1 : Symbol(y1, Decl(abstractPropertyInConstructor.ts, 88, 16))
283+
>this : Symbol(C2, Decl(abstractPropertyInConstructor.ts, 80, 1))
284+
}
285+
}
286+

tests/baselines/reference/abstractPropertyInConstructor.types

+66
Original file line numberDiff line numberDiff line change
@@ -250,3 +250,69 @@ class User {
250250
}
251251
}
252252

253+
abstract class C1 {
254+
>C1 : C1
255+
256+
abstract x: string;
257+
>x : string
258+
259+
abstract y: string;
260+
>y : string
261+
262+
constructor() {
263+
let self = this; // ok
264+
>self : this
265+
>this : this
266+
267+
let { x, y: y1 } = this; // error
268+
>x : string
269+
>y : any
270+
>y1 : string
271+
>this : this
272+
273+
({ x, y: y1, "y": y1 } = this); // error
274+
>({ x, y: y1, "y": y1 } = this) : this
275+
>{ x, y: y1, "y": y1 } = this : this
276+
>{ x, y: y1, "y": y1 } : { x: string; y: string; }
277+
>x : string
278+
>y : string
279+
>y1 : string
280+
>"y" : string
281+
>y1 : string
282+
>this : this
283+
}
284+
}
285+
286+
class C2 {
287+
>C2 : C2
288+
289+
x: string;
290+
>x : string
291+
292+
y: string;
293+
>y : string
294+
295+
constructor() {
296+
let self = this; // ok
297+
>self : this
298+
>this : this
299+
300+
let { x, y: y1 } = this; // ok
301+
>x : string
302+
>y : any
303+
>y1 : string
304+
>this : this
305+
306+
({ x, y: y1, "y": y1 } = this); // ok
307+
>({ x, y: y1, "y": y1 } = this) : this
308+
>{ x, y: y1, "y": y1 } = this : this
309+
>{ x, y: y1, "y": y1 } : { x: string; y: string; }
310+
>x : string
311+
>y : string
312+
>y1 : string
313+
>"y" : string
314+
>y1 : string
315+
>this : this
316+
}
317+
}
318+
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
tests/cases/compiler/destructureComputedProperty.ts(7,7): error TS2341: Property 'p' is private and only accessible within class 'C'.
2-
tests/cases/compiler/destructureComputedProperty.ts(8,7): error TS2341: Property 'p' is private and only accessible within class 'C'.
3-
tests/cases/compiler/destructureComputedProperty.ts(9,7): error TS2341: Property 'p' is private and only accessible within class 'C'.
4-
tests/cases/compiler/destructureComputedProperty.ts(10,7): error TS2341: Property 'p' is private and only accessible within class 'C'.
1+
tests/cases/compiler/destructureComputedProperty.ts(7,9): error TS2341: Property 'p' is private and only accessible within class 'C'.
2+
tests/cases/compiler/destructureComputedProperty.ts(8,9): error TS2341: Property 'p' is private and only accessible within class 'C'.
3+
tests/cases/compiler/destructureComputedProperty.ts(9,9): error TS2341: Property 'p' is private and only accessible within class 'C'.
4+
tests/cases/compiler/destructureComputedProperty.ts(10,9): error TS2341: Property 'p' is private and only accessible within class 'C'.
55

66

77
==== tests/cases/compiler/destructureComputedProperty.ts (4 errors) ====
@@ -12,15 +12,15 @@ tests/cases/compiler/destructureComputedProperty.ts(10,7): error TS2341: Propert
1212
class C { private p: number; }
1313
const nameP = "p";
1414
const { "p": p0 } = new C();
15-
~~~~~~~~~~~
15+
~~~
1616
!!! error TS2341: Property 'p' is private and only accessible within class 'C'.
1717
const { ["p"]: p1 } = new C();
18-
~~~~~~~~~~~~~
18+
~~~~~
1919
!!! error TS2341: Property 'p' is private and only accessible within class 'C'.
2020
const { [nameP]: p2 } = new C();
21-
~~~~~~~~~~~~~~~
21+
~~~~~~~
2222
!!! error TS2341: Property 'p' is private and only accessible within class 'C'.
2323
const { p: p3 } = new C();
24-
~~~~~~~~~
24+
~
2525
!!! error TS2341: Property 'p' is private and only accessible within class 'C'.
2626

Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
tests/cases/conformance/classes/members/accessibility/privateProtectedMembersAreNotAccessibleDestructuring.ts(12,13): error TS2341: Property 'priv' is private and only accessible within class 'K'.
2-
tests/cases/conformance/classes/members/accessibility/privateProtectedMembersAreNotAccessibleDestructuring.ts(17,5): error TS2341: Property 'priv' is private and only accessible within class 'K'.
3-
tests/cases/conformance/classes/members/accessibility/privateProtectedMembersAreNotAccessibleDestructuring.ts(18,5): error TS2445: Property 'prot' is protected and only accessible within class 'K' and its subclasses.
4-
tests/cases/conformance/classes/members/accessibility/privateProtectedMembersAreNotAccessibleDestructuring.ts(19,5): error TS2341: Property 'privateMethod' is private and only accessible within class 'K'.
5-
tests/cases/conformance/classes/members/accessibility/privateProtectedMembersAreNotAccessibleDestructuring.ts(20,5): error TS2341: Property 'priv' is private and only accessible within class 'K'.
6-
tests/cases/conformance/classes/members/accessibility/privateProtectedMembersAreNotAccessibleDestructuring.ts(20,5): error TS2341: Property 'privateMethod' is private and only accessible within class 'K'.
7-
tests/cases/conformance/classes/members/accessibility/privateProtectedMembersAreNotAccessibleDestructuring.ts(20,5): error TS2445: Property 'prot' is protected and only accessible within class 'K' and its subclasses.
8-
tests/cases/conformance/classes/members/accessibility/privateProtectedMembersAreNotAccessibleDestructuring.ts(21,12): error TS2341: Property 'priv' is private and only accessible within class 'K'.
9-
tests/cases/conformance/classes/members/accessibility/privateProtectedMembersAreNotAccessibleDestructuring.ts(21,12): error TS2341: Property 'privateMethod' is private and only accessible within class 'K'.
10-
tests/cases/conformance/classes/members/accessibility/privateProtectedMembersAreNotAccessibleDestructuring.ts(21,12): error TS2445: Property 'prot' is protected and only accessible within class 'K' and its subclasses.
1+
tests/cases/conformance/classes/members/accessibility/privateProtectedMembersAreNotAccessibleDestructuring.ts(12,15): error TS2341: Property 'priv' is private and only accessible within class 'K'.
2+
tests/cases/conformance/classes/members/accessibility/privateProtectedMembersAreNotAccessibleDestructuring.ts(17,7): error TS2341: Property 'priv' is private and only accessible within class 'K'.
3+
tests/cases/conformance/classes/members/accessibility/privateProtectedMembersAreNotAccessibleDestructuring.ts(18,7): error TS2445: Property 'prot' is protected and only accessible within class 'K' and its subclasses.
4+
tests/cases/conformance/classes/members/accessibility/privateProtectedMembersAreNotAccessibleDestructuring.ts(19,7): error TS2341: Property 'privateMethod' is private and only accessible within class 'K'.
5+
tests/cases/conformance/classes/members/accessibility/privateProtectedMembersAreNotAccessibleDestructuring.ts(20,7): error TS2341: Property 'priv' is private and only accessible within class 'K'.
6+
tests/cases/conformance/classes/members/accessibility/privateProtectedMembersAreNotAccessibleDestructuring.ts(20,16): error TS2445: Property 'prot' is protected and only accessible within class 'K' and its subclasses.
7+
tests/cases/conformance/classes/members/accessibility/privateProtectedMembersAreNotAccessibleDestructuring.ts(20,25): error TS2341: Property 'privateMethod' is private and only accessible within class 'K'.
8+
tests/cases/conformance/classes/members/accessibility/privateProtectedMembersAreNotAccessibleDestructuring.ts(21,14): error TS2341: Property 'priv' is private and only accessible within class 'K'.
9+
tests/cases/conformance/classes/members/accessibility/privateProtectedMembersAreNotAccessibleDestructuring.ts(21,20): error TS2445: Property 'prot' is protected and only accessible within class 'K' and its subclasses.
10+
tests/cases/conformance/classes/members/accessibility/privateProtectedMembersAreNotAccessibleDestructuring.ts(21,26): error TS2341: Property 'privateMethod' is private and only accessible within class 'K'.
1111

1212

1313
==== tests/cases/conformance/classes/members/accessibility/privateProtectedMembersAreNotAccessibleDestructuring.ts (10 errors) ====
@@ -23,35 +23,35 @@ tests/cases/conformance/classes/members/accessibility/privateProtectedMembersAre
2323
class C extends K {
2424
m2() {
2525
let { priv: a } = this; // error
26-
~~~~~~~~~~~
26+
~~~~
2727
!!! error TS2341: Property 'priv' is private and only accessible within class 'K'.
2828
let { prot: b } = this; // ok
2929
}
3030
}
3131
let k = new K();
3232
let { priv } = k; // error
33-
~~~~~~~~
33+
~~~~
3434
!!! error TS2341: Property 'priv' is private and only accessible within class 'K'.
3535
let { prot } = k; // error
36-
~~~~~~~~
36+
~~~~
3737
!!! error TS2445: Property 'prot' is protected and only accessible within class 'K' and its subclasses.
3838
let { privateMethod } = k; // error
39-
~~~~~~~~~~~~~~~~~
39+
~~~~~~~~~~~~~
4040
!!! error TS2341: Property 'privateMethod' is private and only accessible within class 'K'.
4141
let { priv: a, prot: b, privateMethod: pm } = k; // error
42-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
42+
~~~~
4343
!!! error TS2341: Property 'priv' is private and only accessible within class 'K'.
44-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
45-
!!! error TS2341: Property 'privateMethod' is private and only accessible within class 'K'.
46-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
44+
~~~~
4745
!!! error TS2445: Property 'prot' is protected and only accessible within class 'K' and its subclasses.
46+
~~~~~~~~~~~~~
47+
!!! error TS2341: Property 'privateMethod' is private and only accessible within class 'K'.
4848
function f({ priv, prot, privateMethod }: K) {
49-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
49+
~~~~
5050
!!! error TS2341: Property 'priv' is private and only accessible within class 'K'.
51-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
52-
!!! error TS2341: Property 'privateMethod' is private and only accessible within class 'K'.
53-
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
51+
~~~~
5452
!!! error TS2445: Property 'prot' is protected and only accessible within class 'K' and its subclasses.
53+
~~~~~~~~~~~~~
54+
!!! error TS2341: Property 'privateMethod' is private and only accessible within class 'K'.
5555

5656
}
5757

0 commit comments

Comments
 (0)