Skip to content

Commit 4942fd2

Browse files
committed
Make checkPropertyNotUsedBeforeDeclaration ignore properties of properties
Use `isAccessExpression` to cover both `PropertyAccess` and `ElementAccess`. Also use it in a few other places that used both explicitly. (And also fix a random weird formatting.) Fixes #32721.
1 parent 291ab63 commit 4942fd2

File tree

5 files changed

+195
-19
lines changed

5 files changed

+195
-19
lines changed

src/compiler/checker.ts

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7619,8 +7619,8 @@ namespace ts {
76197619
return anyType;
76207620
}
76217621
else if (declaration.kind === SyntaxKind.BinaryExpression ||
7622-
(declaration.kind === SyntaxKind.PropertyAccessExpression || declaration.kind === SyntaxKind.ElementAccessExpression) &&
7623-
declaration.parent.kind === SyntaxKind.BinaryExpression) {
7622+
isAccessExpression(declaration) &&
7623+
declaration.parent.kind === SyntaxKind.BinaryExpression) {
76247624
return getWidenedTypeForAssignmentDeclaration(symbol);
76257625
}
76267626
else if (symbol.flags & SymbolFlags.ValueModule && declaration && isSourceFile(declaration) && declaration.commonJsModuleIndicator) {
@@ -21015,8 +21015,8 @@ namespace ts {
2101521015
const parent = func.parent;
2101621016
if (parent.kind === SyntaxKind.BinaryExpression && (<BinaryExpression>parent).operatorToken.kind === SyntaxKind.EqualsToken) {
2101721017
const target = (<BinaryExpression>parent).left;
21018-
if (target.kind === SyntaxKind.PropertyAccessExpression || target.kind === SyntaxKind.ElementAccessExpression) {
21019-
const { expression } = target as AccessExpression;
21018+
if (isAccessExpression(target)) {
21019+
const { expression } = target;
2102021020
// Don't contextually type `this` as `exports` in `exports.Point = function(x, y) { this.x = x; this.y = y; }`
2102121021
if (inJs && isIdentifier(expression)) {
2102221022
const sourceFile = getSourceFileOfNode(parent);
@@ -23208,7 +23208,7 @@ namespace ts {
2320823208
// assignment target, and the referenced property was declared as a variable, property,
2320923209
// accessor, or optional method.
2321023210
const assignmentKind = getAssignmentTargetKind(node);
23211-
if (node.kind !== SyntaxKind.ElementAccessExpression && node.kind !== SyntaxKind.PropertyAccessExpression ||
23211+
if (!isAccessExpression(node) ||
2321223212
assignmentKind === AssignmentKind.Definite ||
2321323213
prop && !(prop.flags & (SymbolFlags.Variable | SymbolFlags.Property | SymbolFlags.Accessor)) && !(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) {
2321423214
return propType;
@@ -23250,8 +23250,9 @@ namespace ts {
2325023250

2325123251
let diagnosticMessage;
2325223252
const declarationName = idText(right);
23253-
if (isInPropertyInitializer(node) &&
23254-
!isBlockScopedNameDeclaredBeforeUse(valueDeclaration, right)
23253+
if (isInPropertyInitializer(node)
23254+
&& !(isAccessExpression(node) && isAccessExpression(node.expression))
23255+
&& !isBlockScopedNameDeclaredBeforeUse(valueDeclaration, right)
2325523256
&& !isPropertyDeclaredInAncestorClass(prop)) {
2325623257
diagnosticMessage = error(right, Diagnostics.Property_0_is_used_before_its_initialization, declarationName);
2325723258
}
@@ -24144,8 +24145,8 @@ namespace ts {
2414424145
function getThisArgumentOfCall(node: CallLikeExpression): LeftHandSideExpression | undefined {
2414524146
if (node.kind === SyntaxKind.CallExpression) {
2414624147
const callee = skipOuterExpressions(node.expression);
24147-
if (callee.kind === SyntaxKind.PropertyAccessExpression || callee.kind === SyntaxKind.ElementAccessExpression) {
24148-
return (callee as AccessExpression).expression;
24148+
if (isAccessExpression(callee)) {
24149+
return callee.expression;
2414924150
}
2415024151
}
2415124152
}
@@ -26499,8 +26500,8 @@ namespace ts {
2649926500
if (isReadonlySymbol(symbol)) {
2650026501
// Allow assignments to readonly properties within constructors of the same class declaration.
2650126502
if (symbol.flags & SymbolFlags.Property &&
26502-
(expr.kind === SyntaxKind.PropertyAccessExpression || expr.kind === SyntaxKind.ElementAccessExpression) &&
26503-
(expr as AccessExpression).expression.kind === SyntaxKind.ThisKeyword) {
26503+
isAccessExpression(expr) &&
26504+
expr.expression.kind === SyntaxKind.ThisKeyword) {
2650426505
// Look for if this is the constructor for the class that `symbol` is a property of.
2650526506
const ctor = getContainingFunction(expr);
2650626507
if (!(ctor && ctor.kind === SyntaxKind.Constructor)) {
@@ -26522,9 +26523,9 @@ namespace ts {
2652226523
}
2652326524
return true;
2652426525
}
26525-
if (expr.kind === SyntaxKind.PropertyAccessExpression || expr.kind === SyntaxKind.ElementAccessExpression) {
26526+
if (isAccessExpression(expr)) {
2652626527
// references through namespace import should be readonly
26527-
const node = skipParentheses((expr as AccessExpression).expression);
26528+
const node = skipParentheses(expr.expression);
2652826529
if (node.kind === SyntaxKind.Identifier) {
2652926530
const symbol = getNodeLinks(node).resolvedSymbol!;
2653026531
if (symbol.flags & SymbolFlags.Alias) {
@@ -26539,7 +26540,7 @@ namespace ts {
2653926540
function checkReferenceExpression(expr: Expression, invalidReferenceMessage: DiagnosticMessage, invalidOptionalChainMessage: DiagnosticMessage): boolean {
2654026541
// References are combinations of identifiers, parentheses, and property accesses.
2654126542
const node = skipOuterExpressions(expr, OuterExpressionKinds.Assertions | OuterExpressionKinds.Parentheses);
26542-
if (node.kind !== SyntaxKind.Identifier && node.kind !== SyntaxKind.PropertyAccessExpression && node.kind !== SyntaxKind.ElementAccessExpression) {
26543+
if (node.kind !== SyntaxKind.Identifier && !isAccessExpression(node)) {
2654326544
error(expr, invalidReferenceMessage);
2654426545
return false;
2654526546
}
@@ -26553,7 +26554,7 @@ namespace ts {
2655326554
function checkDeleteExpression(node: DeleteExpression): Type {
2655426555
checkExpression(node.expression);
2655526556
const expr = skipParentheses(node.expression);
26556-
if (expr.kind !== SyntaxKind.PropertyAccessExpression && expr.kind !== SyntaxKind.ElementAccessExpression) {
26557+
if (!isAccessExpression(expr)) {
2655726558
error(expr, Diagnostics.The_operand_of_a_delete_operator_must_be_a_property_reference);
2655826559
return booleanType;
2655926560
}
@@ -36174,10 +36175,10 @@ namespace ts {
3617436175
}
3617536176

3617636177
function isSimpleLiteralEnumReference(expr: Expression) {
36177-
if (
36178-
(isPropertyAccessExpression(expr) || (isElementAccessExpression(expr) && isStringOrNumberLiteralExpression(expr.argumentExpression))) &&
36179-
isEntityNameExpression(expr.expression)
36180-
) return !!(checkExpressionCached(expr).flags & TypeFlags.EnumLiteral);
36178+
if ((isPropertyAccessExpression(expr) || (isElementAccessExpression(expr) && isStringOrNumberLiteralExpression(expr.argumentExpression))) &&
36179+
isEntityNameExpression(expr.expression)) {
36180+
return !!(checkExpressionCached(expr).flags & TypeFlags.EnumLiteral);
36181+
}
3618136182
}
3618236183

3618336184
function checkAmbientInitializer(node: VariableDeclaration | PropertyDeclaration | PropertySignature) {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//// [recursiveFieldSetting.ts]
2+
// #32721
3+
4+
class Recursive1 {
5+
constructor(private readonly parent?: Recursive1) {}
6+
private depth: number = this.parent ? this.parent.depth + 1 : 0;
7+
}
8+
9+
class Recursive2 {
10+
parent!: Recursive2;
11+
depth: number = this.parent.depth;
12+
}
13+
14+
class Recursive3 {
15+
parent!: Recursive3;
16+
depth: number = this.parent.alpha;
17+
alpha = 0;
18+
}
19+
20+
21+
//// [recursiveFieldSetting.js]
22+
// #32721
23+
var Recursive1 = /** @class */ (function () {
24+
function Recursive1(parent) {
25+
this.parent = parent;
26+
this.depth = this.parent ? this.parent.depth + 1 : 0;
27+
}
28+
return Recursive1;
29+
}());
30+
var Recursive2 = /** @class */ (function () {
31+
function Recursive2() {
32+
this.depth = this.parent.depth;
33+
}
34+
return Recursive2;
35+
}());
36+
var Recursive3 = /** @class */ (function () {
37+
function Recursive3() {
38+
this.depth = this.parent.alpha;
39+
this.alpha = 0;
40+
}
41+
return Recursive3;
42+
}());
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
=== tests/cases/compiler/recursiveFieldSetting.ts ===
2+
// #32721
3+
4+
class Recursive1 {
5+
>Recursive1 : Symbol(Recursive1, Decl(recursiveFieldSetting.ts, 0, 0))
6+
7+
constructor(private readonly parent?: Recursive1) {}
8+
>parent : Symbol(Recursive1.parent, Decl(recursiveFieldSetting.ts, 3, 16))
9+
>Recursive1 : Symbol(Recursive1, Decl(recursiveFieldSetting.ts, 0, 0))
10+
11+
private depth: number = this.parent ? this.parent.depth + 1 : 0;
12+
>depth : Symbol(Recursive1.depth, Decl(recursiveFieldSetting.ts, 3, 56))
13+
>this.parent : Symbol(Recursive1.parent, Decl(recursiveFieldSetting.ts, 3, 16))
14+
>this : Symbol(Recursive1, Decl(recursiveFieldSetting.ts, 0, 0))
15+
>parent : Symbol(Recursive1.parent, Decl(recursiveFieldSetting.ts, 3, 16))
16+
>this.parent.depth : Symbol(Recursive1.depth, Decl(recursiveFieldSetting.ts, 3, 56))
17+
>this.parent : Symbol(Recursive1.parent, Decl(recursiveFieldSetting.ts, 3, 16))
18+
>this : Symbol(Recursive1, Decl(recursiveFieldSetting.ts, 0, 0))
19+
>parent : Symbol(Recursive1.parent, Decl(recursiveFieldSetting.ts, 3, 16))
20+
>depth : Symbol(Recursive1.depth, Decl(recursiveFieldSetting.ts, 3, 56))
21+
}
22+
23+
class Recursive2 {
24+
>Recursive2 : Symbol(Recursive2, Decl(recursiveFieldSetting.ts, 5, 1))
25+
26+
parent!: Recursive2;
27+
>parent : Symbol(Recursive2.parent, Decl(recursiveFieldSetting.ts, 7, 18))
28+
>Recursive2 : Symbol(Recursive2, Decl(recursiveFieldSetting.ts, 5, 1))
29+
30+
depth: number = this.parent.depth;
31+
>depth : Symbol(Recursive2.depth, Decl(recursiveFieldSetting.ts, 8, 24))
32+
>this.parent.depth : Symbol(Recursive2.depth, Decl(recursiveFieldSetting.ts, 8, 24))
33+
>this.parent : Symbol(Recursive2.parent, Decl(recursiveFieldSetting.ts, 7, 18))
34+
>this : Symbol(Recursive2, Decl(recursiveFieldSetting.ts, 5, 1))
35+
>parent : Symbol(Recursive2.parent, Decl(recursiveFieldSetting.ts, 7, 18))
36+
>depth : Symbol(Recursive2.depth, Decl(recursiveFieldSetting.ts, 8, 24))
37+
}
38+
39+
class Recursive3 {
40+
>Recursive3 : Symbol(Recursive3, Decl(recursiveFieldSetting.ts, 10, 1))
41+
42+
parent!: Recursive3;
43+
>parent : Symbol(Recursive3.parent, Decl(recursiveFieldSetting.ts, 12, 18))
44+
>Recursive3 : Symbol(Recursive3, Decl(recursiveFieldSetting.ts, 10, 1))
45+
46+
depth: number = this.parent.alpha;
47+
>depth : Symbol(Recursive3.depth, Decl(recursiveFieldSetting.ts, 13, 24))
48+
>this.parent.alpha : Symbol(Recursive3.alpha, Decl(recursiveFieldSetting.ts, 14, 38))
49+
>this.parent : Symbol(Recursive3.parent, Decl(recursiveFieldSetting.ts, 12, 18))
50+
>this : Symbol(Recursive3, Decl(recursiveFieldSetting.ts, 10, 1))
51+
>parent : Symbol(Recursive3.parent, Decl(recursiveFieldSetting.ts, 12, 18))
52+
>alpha : Symbol(Recursive3.alpha, Decl(recursiveFieldSetting.ts, 14, 38))
53+
54+
alpha = 0;
55+
>alpha : Symbol(Recursive3.alpha, Decl(recursiveFieldSetting.ts, 14, 38))
56+
}
57+
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
=== tests/cases/compiler/recursiveFieldSetting.ts ===
2+
// #32721
3+
4+
class Recursive1 {
5+
>Recursive1 : Recursive1
6+
7+
constructor(private readonly parent?: Recursive1) {}
8+
>parent : Recursive1
9+
10+
private depth: number = this.parent ? this.parent.depth + 1 : 0;
11+
>depth : number
12+
>this.parent ? this.parent.depth + 1 : 0 : number
13+
>this.parent : Recursive1
14+
>this : this
15+
>parent : Recursive1
16+
>this.parent.depth + 1 : number
17+
>this.parent.depth : number
18+
>this.parent : Recursive1
19+
>this : this
20+
>parent : Recursive1
21+
>depth : number
22+
>1 : 1
23+
>0 : 0
24+
}
25+
26+
class Recursive2 {
27+
>Recursive2 : Recursive2
28+
29+
parent!: Recursive2;
30+
>parent : Recursive2
31+
32+
depth: number = this.parent.depth;
33+
>depth : number
34+
>this.parent.depth : number
35+
>this.parent : Recursive2
36+
>this : this
37+
>parent : Recursive2
38+
>depth : number
39+
}
40+
41+
class Recursive3 {
42+
>Recursive3 : Recursive3
43+
44+
parent!: Recursive3;
45+
>parent : Recursive3
46+
47+
depth: number = this.parent.alpha;
48+
>depth : number
49+
>this.parent.alpha : number
50+
>this.parent : Recursive3
51+
>this : this
52+
>parent : Recursive3
53+
>alpha : number
54+
55+
alpha = 0;
56+
>alpha : number
57+
>0 : 0
58+
}
59+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
// #32721
2+
3+
class Recursive1 {
4+
constructor(private readonly parent?: Recursive1) {}
5+
private depth: number = this.parent ? this.parent.depth + 1 : 0;
6+
}
7+
8+
class Recursive2 {
9+
parent!: Recursive2;
10+
depth: number = this.parent.depth;
11+
}
12+
13+
class Recursive3 {
14+
parent!: Recursive3;
15+
depth: number = this.parent.alpha;
16+
alpha = 0;
17+
}

0 commit comments

Comments
 (0)