diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index f5eb12a419..a8b6557e3c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8920,6 +8920,7 @@ namespace ts { const isParameter = getRootDeclaration(declaration).kind === SyntaxKind.Parameter; const declarationContainer = getControlFlowContainer(declaration); let flowContainer = getControlFlowContainer(node); + const isOuterVariable = flowContainer !== declarationContainer; // When the control flow originates in a function expression or arrow function and we are referencing // a const variable or parameter from an outer function, we extend the origin of the control flow // analysis to include the immediately enclosing function. @@ -8932,7 +8933,7 @@ namespace ts { // the entire control flow graph from the variable's declaration (i.e. when the flow container and // declaration container are the same). const assumeInitialized = !strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || isParameter || - flowContainer !== declarationContainer || isInAmbientContext(declaration); + isOuterVariable || isInAmbientContext(declaration); const flowType = getFlowTypeOfReference(node, type, assumeInitialized, flowContainer); // A variable is considered uninitialized when it is possible to analyze the entire control flow graph // from declaration to use, and when the variable's declared type doesn't include undefined but the diff --git a/tests/baselines/reference/controlFlowOuterVariable.js b/tests/baselines/reference/controlFlowOuterVariable.js new file mode 100644 index 0000000000..1925d75eb3 --- /dev/null +++ b/tests/baselines/reference/controlFlowOuterVariable.js @@ -0,0 +1,26 @@ +//// [controlFlowOuterVariable.ts] + +// Repros from #10641 + +const CONFIG = { + foo: '', + setFoo: function(foo: string) { + CONFIG.foo = foo; + } +}; + +const helper = function(t: T[]) { + helper(t.slice(1)); +} + +//// [controlFlowOuterVariable.js] +// Repros from #10641 +var CONFIG = { + foo: '', + setFoo: function (foo) { + CONFIG.foo = foo; + } +}; +var helper = function (t) { + helper(t.slice(1)); +}; diff --git a/tests/baselines/reference/controlFlowOuterVariable.symbols b/tests/baselines/reference/controlFlowOuterVariable.symbols new file mode 100644 index 0000000000..35974f28a8 --- /dev/null +++ b/tests/baselines/reference/controlFlowOuterVariable.symbols @@ -0,0 +1,34 @@ +=== tests/cases/compiler/controlFlowOuterVariable.ts === + +// Repros from #10641 + +const CONFIG = { +>CONFIG : Symbol(CONFIG, Decl(controlFlowOuterVariable.ts, 3, 5)) + + foo: '', +>foo : Symbol(foo, Decl(controlFlowOuterVariable.ts, 3, 16)) + + setFoo: function(foo: string) { +>setFoo : Symbol(setFoo, Decl(controlFlowOuterVariable.ts, 4, 12)) +>foo : Symbol(foo, Decl(controlFlowOuterVariable.ts, 5, 21)) + + CONFIG.foo = foo; +>CONFIG.foo : Symbol(foo, Decl(controlFlowOuterVariable.ts, 3, 16)) +>CONFIG : Symbol(CONFIG, Decl(controlFlowOuterVariable.ts, 3, 5)) +>foo : Symbol(foo, Decl(controlFlowOuterVariable.ts, 3, 16)) +>foo : Symbol(foo, Decl(controlFlowOuterVariable.ts, 5, 21)) + } +}; + +const helper = function(t: T[]) { +>helper : Symbol(helper, Decl(controlFlowOuterVariable.ts, 10, 5)) +>T : Symbol(T, Decl(controlFlowOuterVariable.ts, 10, 24)) +>t : Symbol(t, Decl(controlFlowOuterVariable.ts, 10, 27)) +>T : Symbol(T, Decl(controlFlowOuterVariable.ts, 10, 24)) + + helper(t.slice(1)); +>helper : Symbol(helper, Decl(controlFlowOuterVariable.ts, 10, 5)) +>t.slice : Symbol(Array.slice, Decl(lib.d.ts, --, --)) +>t : Symbol(t, Decl(controlFlowOuterVariable.ts, 10, 27)) +>slice : Symbol(Array.slice, Decl(lib.d.ts, --, --)) +} diff --git a/tests/baselines/reference/controlFlowOuterVariable.types b/tests/baselines/reference/controlFlowOuterVariable.types new file mode 100644 index 0000000000..602776dd54 --- /dev/null +++ b/tests/baselines/reference/controlFlowOuterVariable.types @@ -0,0 +1,42 @@ +=== tests/cases/compiler/controlFlowOuterVariable.ts === + +// Repros from #10641 + +const CONFIG = { +>CONFIG : { foo: string; setFoo: (foo: string) => void; } +>{ foo: '', setFoo: function(foo: string) { CONFIG.foo = foo; }} : { foo: string; setFoo: (foo: string) => void; } + + foo: '', +>foo : string +>'' : string + + setFoo: function(foo: string) { +>setFoo : (foo: string) => void +>function(foo: string) { CONFIG.foo = foo; } : (foo: string) => void +>foo : string + + CONFIG.foo = foo; +>CONFIG.foo = foo : string +>CONFIG.foo : string +>CONFIG : { foo: string; setFoo: (foo: string) => void; } +>foo : string +>foo : string + } +}; + +const helper = function(t: T[]) { +>helper : (t: T[]) => void +>function(t: T[]) { helper(t.slice(1));} : (t: T[]) => void +>T : T +>t : T[] +>T : T + + helper(t.slice(1)); +>helper(t.slice(1)) : void +>helper : (t: T[]) => void +>t.slice(1) : T[] +>t.slice : (start?: number | undefined, end?: number | undefined) => T[] +>t : T[] +>slice : (start?: number | undefined, end?: number | undefined) => T[] +>1 : number +} diff --git a/tests/cases/compiler/controlFlowOuterVariable.ts b/tests/cases/compiler/controlFlowOuterVariable.ts new file mode 100644 index 0000000000..3a33f3b25e --- /dev/null +++ b/tests/cases/compiler/controlFlowOuterVariable.ts @@ -0,0 +1,14 @@ +// @strictNullChecks: true + +// Repros from #10641 + +const CONFIG = { + foo: '', + setFoo: function(foo: string) { + CONFIG.foo = foo; + } +}; + +const helper = function(t: T[]) { + helper(t.slice(1)); +} \ No newline at end of file