Skip to content

Commit 1ca99b3

Browse files
authoredOct 20, 2022
fix(50551): Destructuring assignment with var bypasses "variable is used before being assigned" check (2454) (#50560)
* fix(50551): handle destructuring variables used before assignment * skip the error in binding elements that refer to the same destructuring * fix binding element type
1 parent 3f28fa1 commit 1ca99b3

10 files changed

+276
-1
lines changed
 

‎src/compiler/checker.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -26289,7 +26289,7 @@ namespace ts {
2628926289
// We only look for uninitialized variables in strict null checking mode, and only when we can analyze
2629026290
// the entire control flow graph from the variable's declaration (i.e. when the flow container and
2629126291
// declaration container are the same).
26292-
const assumeInitialized = isParameter || isAlias || isOuterVariable || isSpreadDestructuringAssignmentTarget || isModuleExports || isBindingElement(declaration) ||
26292+
const assumeInitialized = isParameter || isAlias || isOuterVariable || isSpreadDestructuringAssignmentTarget || isModuleExports || isSameScopedBindingElement(node, declaration) ||
2629326293
type !== autoType && type !== autoArrayType && (!strictNullChecks || (type.flags & (TypeFlags.AnyOrUnknown | TypeFlags.Void)) !== 0 ||
2629426294
isInTypeQuery(node) || node.parent.kind === SyntaxKind.ExportSpecifier) ||
2629526295
node.parent.kind === SyntaxKind.NonNullExpression ||
@@ -26319,6 +26319,13 @@ namespace ts {
2631926319
return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType;
2632026320
}
2632126321

26322+
function isSameScopedBindingElement(node: Identifier, declaration: Declaration) {
26323+
if (isBindingElement(declaration)) {
26324+
const bindingElement = findAncestor(node, isBindingElement);
26325+
return bindingElement && getRootDeclaration(bindingElement) === getRootDeclaration(declaration);
26326+
}
26327+
}
26328+
2632226329
function shouldMarkIdentifierAliasReferenced(node: Identifier): boolean {
2632326330
const parent = node.parent;
2632426331
if (parent) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
tests/cases/compiler/controlFlowDestructuringVariablesInTryCatch.ts(16,1): error TS2454: Variable 'a' is used before being assigned.
2+
tests/cases/compiler/controlFlowDestructuringVariablesInTryCatch.ts(17,1): error TS2454: Variable 'b' is used before being assigned.
3+
tests/cases/compiler/controlFlowDestructuringVariablesInTryCatch.ts(18,1): error TS2454: Variable 'c' is used before being assigned.
4+
tests/cases/compiler/controlFlowDestructuringVariablesInTryCatch.ts(19,1): error TS2454: Variable 'd' is used before being assigned.
5+
tests/cases/compiler/controlFlowDestructuringVariablesInTryCatch.ts(20,1): error TS2454: Variable 'e' is used before being assigned.
6+
7+
8+
==== tests/cases/compiler/controlFlowDestructuringVariablesInTryCatch.ts (5 errors) ====
9+
declare function f1(): string;
10+
declare function f2(): [b: string];
11+
declare function f3(): { c: string };
12+
13+
try {
14+
var a = f1();
15+
var [b] = f2();
16+
var { c } = f3();
17+
18+
var [d = 1] = [];
19+
var { e = 1 } = { };
20+
} catch {
21+
console.error("error");
22+
}
23+
24+
a;
25+
~
26+
!!! error TS2454: Variable 'a' is used before being assigned.
27+
b;
28+
~
29+
!!! error TS2454: Variable 'b' is used before being assigned.
30+
c;
31+
~
32+
!!! error TS2454: Variable 'c' is used before being assigned.
33+
d;
34+
~
35+
!!! error TS2454: Variable 'd' is used before being assigned.
36+
e;
37+
~
38+
!!! error TS2454: Variable 'e' is used before being assigned.
39+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//// [controlFlowDestructuringVariablesInTryCatch.ts]
2+
declare function f1(): string;
3+
declare function f2(): [b: string];
4+
declare function f3(): { c: string };
5+
6+
try {
7+
var a = f1();
8+
var [b] = f2();
9+
var { c } = f3();
10+
11+
var [d = 1] = [];
12+
var { e = 1 } = { };
13+
} catch {
14+
console.error("error");
15+
}
16+
17+
a;
18+
b;
19+
c;
20+
d;
21+
e;
22+
23+
24+
//// [controlFlowDestructuringVariablesInTryCatch.js]
25+
"use strict";
26+
try {
27+
var a = f1();
28+
var b = f2()[0];
29+
var c = f3().c;
30+
var _a = [][0], d = _a === void 0 ? 1 : _a;
31+
var _b = {}.e, e = _b === void 0 ? 1 : _b;
32+
}
33+
catch (_c) {
34+
console.error("error");
35+
}
36+
a;
37+
b;
38+
c;
39+
d;
40+
e;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
=== tests/cases/compiler/controlFlowDestructuringVariablesInTryCatch.ts ===
2+
declare function f1(): string;
3+
>f1 : Symbol(f1, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 0, 0))
4+
5+
declare function f2(): [b: string];
6+
>f2 : Symbol(f2, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 0, 30))
7+
8+
declare function f3(): { c: string };
9+
>f3 : Symbol(f3, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 1, 35))
10+
>c : Symbol(c, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 2, 24))
11+
12+
try {
13+
var a = f1();
14+
>a : Symbol(a, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 5, 7))
15+
>f1 : Symbol(f1, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 0, 0))
16+
17+
var [b] = f2();
18+
>b : Symbol(b, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 6, 9))
19+
>f2 : Symbol(f2, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 0, 30))
20+
21+
var { c } = f3();
22+
>c : Symbol(c, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 7, 9))
23+
>f3 : Symbol(f3, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 1, 35))
24+
25+
var [d = 1] = [];
26+
>d : Symbol(d, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 9, 9))
27+
28+
var { e = 1 } = { };
29+
>e : Symbol(e, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 10, 9))
30+
31+
} catch {
32+
console.error("error");
33+
>console.error : Symbol(Console.error, Decl(lib.dom.d.ts, --, --))
34+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
35+
>error : Symbol(Console.error, Decl(lib.dom.d.ts, --, --))
36+
}
37+
38+
a;
39+
>a : Symbol(a, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 5, 7))
40+
41+
b;
42+
>b : Symbol(b, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 6, 9))
43+
44+
c;
45+
>c : Symbol(c, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 7, 9))
46+
47+
d;
48+
>d : Symbol(d, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 9, 9))
49+
50+
e;
51+
>e : Symbol(e, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 10, 9))
52+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
=== tests/cases/compiler/controlFlowDestructuringVariablesInTryCatch.ts ===
2+
declare function f1(): string;
3+
>f1 : () => string
4+
5+
declare function f2(): [b: string];
6+
>f2 : () => [b: string]
7+
8+
declare function f3(): { c: string };
9+
>f3 : () => { c: string; }
10+
>c : string
11+
12+
try {
13+
var a = f1();
14+
>a : string
15+
>f1() : string
16+
>f1 : () => string
17+
18+
var [b] = f2();
19+
>b : string
20+
>f2() : [b: string]
21+
>f2 : () => [b: string]
22+
23+
var { c } = f3();
24+
>c : string
25+
>f3() : { c: string; }
26+
>f3 : () => { c: string; }
27+
28+
var [d = 1] = [];
29+
>d : number
30+
>1 : 1
31+
>[] : []
32+
33+
var { e = 1 } = { };
34+
>e : number
35+
>1 : 1
36+
>{ } : { e?: number | undefined; }
37+
38+
} catch {
39+
console.error("error");
40+
>console.error("error") : void
41+
>console.error : (...data: any[]) => void
42+
>console : Console
43+
>error : (...data: any[]) => void
44+
>"error" : "error"
45+
}
46+
47+
a;
48+
>a : string
49+
50+
b;
51+
>b : string
52+
53+
c;
54+
>c : string
55+
56+
d;
57+
>d : number
58+
59+
e;
60+
>e : number
61+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//// [controlFlowInitializedDestructuringVariables.ts]
2+
declare const obj: { a?: string, b?: number };
3+
const {
4+
a = "0",
5+
b = +a,
6+
} = obj;
7+
8+
9+
//// [controlFlowInitializedDestructuringVariables.js]
10+
"use strict";
11+
var _a = obj.a, a = _a === void 0 ? "0" : _a, _b = obj.b, b = _b === void 0 ? +a : _b;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
=== tests/cases/compiler/controlFlowInitializedDestructuringVariables.ts ===
2+
declare const obj: { a?: string, b?: number };
3+
>obj : Symbol(obj, Decl(controlFlowInitializedDestructuringVariables.ts, 0, 13))
4+
>a : Symbol(a, Decl(controlFlowInitializedDestructuringVariables.ts, 0, 20))
5+
>b : Symbol(b, Decl(controlFlowInitializedDestructuringVariables.ts, 0, 32))
6+
7+
const {
8+
a = "0",
9+
>a : Symbol(a, Decl(controlFlowInitializedDestructuringVariables.ts, 1, 7))
10+
11+
b = +a,
12+
>b : Symbol(b, Decl(controlFlowInitializedDestructuringVariables.ts, 2, 12))
13+
>a : Symbol(a, Decl(controlFlowInitializedDestructuringVariables.ts, 1, 7))
14+
15+
} = obj;
16+
>obj : Symbol(obj, Decl(controlFlowInitializedDestructuringVariables.ts, 0, 13))
17+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
=== tests/cases/compiler/controlFlowInitializedDestructuringVariables.ts ===
2+
declare const obj: { a?: string, b?: number };
3+
>obj : { a?: string | undefined; b?: number | undefined; }
4+
>a : string | undefined
5+
>b : number | undefined
6+
7+
const {
8+
a = "0",
9+
>a : string
10+
>"0" : "0"
11+
12+
b = +a,
13+
>b : number
14+
>+a : number
15+
>a : string
16+
17+
} = obj;
18+
>obj : { a?: string | undefined; b?: number | undefined; }
19+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// @strict: true
2+
3+
declare function f1(): string;
4+
declare function f2(): [b: string];
5+
declare function f3(): { c: string };
6+
7+
try {
8+
var a = f1();
9+
var [b] = f2();
10+
var { c } = f3();
11+
12+
var [d = 1] = [];
13+
var { e = 1 } = { };
14+
} catch {
15+
console.error("error");
16+
}
17+
18+
a;
19+
b;
20+
c;
21+
d;
22+
e;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// @strict: true
2+
3+
declare const obj: { a?: string, b?: number };
4+
const {
5+
a = "0",
6+
b = +a,
7+
} = obj;

0 commit comments

Comments
 (0)
Please sign in to comment.