diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3c7b1060eae86..598a699459e54 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -26289,7 +26289,7 @@ namespace ts { // We only look for uninitialized variables in strict null checking mode, and only when we can analyze // the entire control flow graph from the variable's declaration (i.e. when the flow container and // declaration container are the same). - const assumeInitialized = isParameter || isAlias || isOuterVariable || isSpreadDestructuringAssignmentTarget || isModuleExports || isBindingElement(declaration) || + const assumeInitialized = isParameter || isAlias || isOuterVariable || isSpreadDestructuringAssignmentTarget || isModuleExports || isSameScopedBindingElement(node, declaration) || type !== autoType && type !== autoArrayType && (!strictNullChecks || (type.flags & (TypeFlags.AnyOrUnknown | TypeFlags.Void)) !== 0 || isInTypeQuery(node) || node.parent.kind === SyntaxKind.ExportSpecifier) || node.parent.kind === SyntaxKind.NonNullExpression || @@ -26319,6 +26319,13 @@ namespace ts { return assignmentKind ? getBaseTypeOfLiteralType(flowType) : flowType; } + function isSameScopedBindingElement(node: Identifier, declaration: Declaration) { + if (isBindingElement(declaration)) { + const bindingElement = findAncestor(node, isBindingElement); + return bindingElement && getRootDeclaration(bindingElement) === getRootDeclaration(declaration); + } + } + function shouldMarkIdentifierAliasReferenced(node: Identifier): boolean { const parent = node.parent; if (parent) { diff --git a/tests/baselines/reference/controlFlowDestructuringVariablesInTryCatch.errors.txt b/tests/baselines/reference/controlFlowDestructuringVariablesInTryCatch.errors.txt new file mode 100644 index 0000000000000..dbc664da588da --- /dev/null +++ b/tests/baselines/reference/controlFlowDestructuringVariablesInTryCatch.errors.txt @@ -0,0 +1,39 @@ +tests/cases/compiler/controlFlowDestructuringVariablesInTryCatch.ts(16,1): error TS2454: Variable 'a' is used before being assigned. +tests/cases/compiler/controlFlowDestructuringVariablesInTryCatch.ts(17,1): error TS2454: Variable 'b' is used before being assigned. +tests/cases/compiler/controlFlowDestructuringVariablesInTryCatch.ts(18,1): error TS2454: Variable 'c' is used before being assigned. +tests/cases/compiler/controlFlowDestructuringVariablesInTryCatch.ts(19,1): error TS2454: Variable 'd' is used before being assigned. +tests/cases/compiler/controlFlowDestructuringVariablesInTryCatch.ts(20,1): error TS2454: Variable 'e' is used before being assigned. + + +==== tests/cases/compiler/controlFlowDestructuringVariablesInTryCatch.ts (5 errors) ==== + declare function f1(): string; + declare function f2(): [b: string]; + declare function f3(): { c: string }; + + try { + var a = f1(); + var [b] = f2(); + var { c } = f3(); + + var [d = 1] = []; + var { e = 1 } = { }; + } catch { + console.error("error"); + } + + a; + ~ +!!! error TS2454: Variable 'a' is used before being assigned. + b; + ~ +!!! error TS2454: Variable 'b' is used before being assigned. + c; + ~ +!!! error TS2454: Variable 'c' is used before being assigned. + d; + ~ +!!! error TS2454: Variable 'd' is used before being assigned. + e; + ~ +!!! error TS2454: Variable 'e' is used before being assigned. + \ No newline at end of file diff --git a/tests/baselines/reference/controlFlowDestructuringVariablesInTryCatch.js b/tests/baselines/reference/controlFlowDestructuringVariablesInTryCatch.js new file mode 100644 index 0000000000000..a64aba170d6dd --- /dev/null +++ b/tests/baselines/reference/controlFlowDestructuringVariablesInTryCatch.js @@ -0,0 +1,40 @@ +//// [controlFlowDestructuringVariablesInTryCatch.ts] +declare function f1(): string; +declare function f2(): [b: string]; +declare function f3(): { c: string }; + +try { + var a = f1(); + var [b] = f2(); + var { c } = f3(); + + var [d = 1] = []; + var { e = 1 } = { }; +} catch { + console.error("error"); +} + +a; +b; +c; +d; +e; + + +//// [controlFlowDestructuringVariablesInTryCatch.js] +"use strict"; +try { + var a = f1(); + var b = f2()[0]; + var c = f3().c; + var _a = [][0], d = _a === void 0 ? 1 : _a; + var _b = {}.e, e = _b === void 0 ? 1 : _b; +} +catch (_c) { + console.error("error"); +} +a; +b; +c; +d; +e; diff --git a/tests/baselines/reference/controlFlowDestructuringVariablesInTryCatch.symbols b/tests/baselines/reference/controlFlowDestructuringVariablesInTryCatch.symbols new file mode 100644 index 0000000000000..c41f27f9ef62f --- /dev/null +++ b/tests/baselines/reference/controlFlowDestructuringVariablesInTryCatch.symbols @@ -0,0 +1,52 @@ +=== tests/cases/compiler/controlFlowDestructuringVariablesInTryCatch.ts === +declare function f1(): string; +>f1 : Symbol(f1, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 0, 0)) + +declare function f2(): [b: string]; +>f2 : Symbol(f2, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 0, 30)) + +declare function f3(): { c: string }; +>f3 : Symbol(f3, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 1, 35)) +>c : Symbol(c, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 2, 24)) + +try { + var a = f1(); +>a : Symbol(a, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 5, 7)) +>f1 : Symbol(f1, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 0, 0)) + + var [b] = f2(); +>b : Symbol(b, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 6, 9)) +>f2 : Symbol(f2, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 0, 30)) + + var { c } = f3(); +>c : Symbol(c, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 7, 9)) +>f3 : Symbol(f3, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 1, 35)) + + var [d = 1] = []; +>d : Symbol(d, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 9, 9)) + + var { e = 1 } = { }; +>e : Symbol(e, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 10, 9)) + +} catch { + console.error("error"); +>console.error : Symbol(Console.error, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>error : Symbol(Console.error, Decl(lib.dom.d.ts, --, --)) +} + +a; +>a : Symbol(a, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 5, 7)) + +b; +>b : Symbol(b, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 6, 9)) + +c; +>c : Symbol(c, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 7, 9)) + +d; +>d : Symbol(d, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 9, 9)) + +e; +>e : Symbol(e, Decl(controlFlowDestructuringVariablesInTryCatch.ts, 10, 9)) + diff --git a/tests/baselines/reference/controlFlowDestructuringVariablesInTryCatch.types b/tests/baselines/reference/controlFlowDestructuringVariablesInTryCatch.types new file mode 100644 index 0000000000000..2cf5b8d5ec372 --- /dev/null +++ b/tests/baselines/reference/controlFlowDestructuringVariablesInTryCatch.types @@ -0,0 +1,61 @@ +=== tests/cases/compiler/controlFlowDestructuringVariablesInTryCatch.ts === +declare function f1(): string; +>f1 : () => string + +declare function f2(): [b: string]; +>f2 : () => [b: string] + +declare function f3(): { c: string }; +>f3 : () => { c: string; } +>c : string + +try { + var a = f1(); +>a : string +>f1() : string +>f1 : () => string + + var [b] = f2(); +>b : string +>f2() : [b: string] +>f2 : () => [b: string] + + var { c } = f3(); +>c : string +>f3() : { c: string; } +>f3 : () => { c: string; } + + var [d = 1] = []; +>d : number +>1 : 1 +>[] : [] + + var { e = 1 } = { }; +>e : number +>1 : 1 +>{ } : { e?: number | undefined; } + +} catch { + console.error("error"); +>console.error("error") : void +>console.error : (...data: any[]) => void +>console : Console +>error : (...data: any[]) => void +>"error" : "error" +} + +a; +>a : string + +b; +>b : string + +c; +>c : string + +d; +>d : number + +e; +>e : number + diff --git a/tests/baselines/reference/controlFlowInitializedDestructuringVariables.js b/tests/baselines/reference/controlFlowInitializedDestructuringVariables.js new file mode 100644 index 0000000000000..843b6939b2562 --- /dev/null +++ b/tests/baselines/reference/controlFlowInitializedDestructuringVariables.js @@ -0,0 +1,11 @@ +//// [controlFlowInitializedDestructuringVariables.ts] +declare const obj: { a?: string, b?: number }; +const { + a = "0", + b = +a, +} = obj; + + +//// [controlFlowInitializedDestructuringVariables.js] +"use strict"; +var _a = obj.a, a = _a === void 0 ? "0" : _a, _b = obj.b, b = _b === void 0 ? +a : _b; diff --git a/tests/baselines/reference/controlFlowInitializedDestructuringVariables.symbols b/tests/baselines/reference/controlFlowInitializedDestructuringVariables.symbols new file mode 100644 index 0000000000000..f664c9db2b9d0 --- /dev/null +++ b/tests/baselines/reference/controlFlowInitializedDestructuringVariables.symbols @@ -0,0 +1,17 @@ +=== tests/cases/compiler/controlFlowInitializedDestructuringVariables.ts === +declare const obj: { a?: string, b?: number }; +>obj : Symbol(obj, Decl(controlFlowInitializedDestructuringVariables.ts, 0, 13)) +>a : Symbol(a, Decl(controlFlowInitializedDestructuringVariables.ts, 0, 20)) +>b : Symbol(b, Decl(controlFlowInitializedDestructuringVariables.ts, 0, 32)) + +const { + a = "0", +>a : Symbol(a, Decl(controlFlowInitializedDestructuringVariables.ts, 1, 7)) + + b = +a, +>b : Symbol(b, Decl(controlFlowInitializedDestructuringVariables.ts, 2, 12)) +>a : Symbol(a, Decl(controlFlowInitializedDestructuringVariables.ts, 1, 7)) + +} = obj; +>obj : Symbol(obj, Decl(controlFlowInitializedDestructuringVariables.ts, 0, 13)) + diff --git a/tests/baselines/reference/controlFlowInitializedDestructuringVariables.types b/tests/baselines/reference/controlFlowInitializedDestructuringVariables.types new file mode 100644 index 0000000000000..8c64c50a3275c --- /dev/null +++ b/tests/baselines/reference/controlFlowInitializedDestructuringVariables.types @@ -0,0 +1,19 @@ +=== tests/cases/compiler/controlFlowInitializedDestructuringVariables.ts === +declare const obj: { a?: string, b?: number }; +>obj : { a?: string | undefined; b?: number | undefined; } +>a : string | undefined +>b : number | undefined + +const { + a = "0", +>a : string +>"0" : "0" + + b = +a, +>b : number +>+a : number +>a : string + +} = obj; +>obj : { a?: string | undefined; b?: number | undefined; } + diff --git a/tests/cases/compiler/controlFlowDestructuringVariablesInTryCatch.ts b/tests/cases/compiler/controlFlowDestructuringVariablesInTryCatch.ts new file mode 100644 index 0000000000000..a3dd7e4f810db --- /dev/null +++ b/tests/cases/compiler/controlFlowDestructuringVariablesInTryCatch.ts @@ -0,0 +1,22 @@ +// @strict: true + +declare function f1(): string; +declare function f2(): [b: string]; +declare function f3(): { c: string }; + +try { + var a = f1(); + var [b] = f2(); + var { c } = f3(); + + var [d = 1] = []; + var { e = 1 } = { }; +} catch { + console.error("error"); +} + +a; +b; +c; +d; +e; diff --git a/tests/cases/compiler/controlFlowInitializedDestructuringVariables.ts b/tests/cases/compiler/controlFlowInitializedDestructuringVariables.ts new file mode 100644 index 0000000000000..d87dba83a0f42 --- /dev/null +++ b/tests/cases/compiler/controlFlowInitializedDestructuringVariables.ts @@ -0,0 +1,7 @@ +// @strict: true + +declare const obj: { a?: string, b?: number }; +const { + a = "0", + b = +a, +} = obj;