@@ -531,7 +531,7 @@ namespace ts {
531531
532532 function getNodeLinks(node: Node): NodeLinks {
533533 const nodeId = getNodeId(node);
534- return nodeLinks[nodeId] || (nodeLinks[nodeId] = {});
534+ return nodeLinks[nodeId] || (nodeLinks[nodeId] = { flags: 0 });
535535 }
536536
537537 function isGlobalSourceFile(node: Node) {
@@ -8192,7 +8192,7 @@ namespace ts {
81928192 return incomplete ? { flags: 0, type } : type;
81938193 }
81948194
8195- function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, includeOuterFunctions: boolean ) {
8195+ function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, flowContainer: Node ) {
81968196 let key: string;
81978197 if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
81988198 return declaredType;
@@ -8244,7 +8244,7 @@ namespace ts {
82448244 else if (flow.flags & FlowFlags.Start) {
82458245 // Check if we should continue with the control flow of the containing function.
82468246 const container = (<FlowStart>flow).container;
8247- if (container && includeOuterFunctions ) {
8247+ if (container && container !== flowContainer && reference.kind !== SyntaxKind.PropertyAccessExpression ) {
82488248 flow = container.flowNode;
82498249 continue;
82508250 }
@@ -8722,21 +8722,52 @@ namespace ts {
87228722 function getControlFlowContainer(node: Node): Node {
87238723 while (true) {
87248724 node = node.parent;
8725- if (isFunctionLike(node) || node.kind === SyntaxKind.ModuleBlock || node.kind === SyntaxKind.SourceFile || node.kind === SyntaxKind.PropertyDeclaration) {
8725+ if (isFunctionLike(node) && !getImmediatelyInvokedFunctionExpression(node) ||
8726+ node.kind === SyntaxKind.ModuleBlock ||
8727+ node.kind === SyntaxKind.SourceFile ||
8728+ node.kind === SyntaxKind.PropertyDeclaration) {
87268729 return node;
87278730 }
87288731 }
87298732 }
87308733
8731- function isDeclarationIncludedInFlow(reference: Node, declaration: Declaration, includeOuterFunctions: boolean) {
8732- const declarationContainer = getControlFlowContainer(declaration);
8733- let container = getControlFlowContainer(reference);
8734- while (container !== declarationContainer &&
8735- (container.kind === SyntaxKind.FunctionExpression || container.kind === SyntaxKind.ArrowFunction) &&
8736- (includeOuterFunctions || getImmediatelyInvokedFunctionExpression(<FunctionExpression>container))) {
8737- container = getControlFlowContainer(container);
8734+ // Check if a parameter is assigned anywhere within its declaring function.
8735+ function isParameterAssigned(symbol: Symbol) {
8736+ const func = <FunctionLikeDeclaration>getRootDeclaration(symbol.valueDeclaration).parent;
8737+ const links = getNodeLinks(func);
8738+ if (!(links.flags & NodeCheckFlags.AssignmentsMarked)) {
8739+ links.flags |= NodeCheckFlags.AssignmentsMarked;
8740+ if (!hasParentWithAssignmentsMarked(func)) {
8741+ markParameterAssignments(func);
8742+ }
8743+ }
8744+ return symbol.isAssigned || false;
8745+ }
8746+
8747+ function hasParentWithAssignmentsMarked(node: Node) {
8748+ while (true) {
8749+ node = node.parent;
8750+ if (!node) {
8751+ return false;
8752+ }
8753+ if (isFunctionLike(node) && getNodeLinks(node).flags & NodeCheckFlags.AssignmentsMarked) {
8754+ return true;
8755+ }
8756+ }
8757+ }
8758+
8759+ function markParameterAssignments(node: Node) {
8760+ if (node.kind === SyntaxKind.Identifier) {
8761+ if (isAssignmentTarget(node)) {
8762+ const symbol = getResolvedSymbol(<Identifier>node);
8763+ if (symbol.valueDeclaration && getRootDeclaration(symbol.valueDeclaration).kind === SyntaxKind.Parameter) {
8764+ symbol.isAssigned = true;
8765+ }
8766+ }
8767+ }
8768+ else {
8769+ forEachChild(node, markParameterAssignments);
87388770 }
8739- return container === declarationContainer;
87408771 }
87418772
87428773 function checkIdentifier(node: Identifier): Type {
@@ -8791,15 +8822,35 @@ namespace ts {
87918822 checkNestedBlockScopedBinding(node, symbol);
87928823
87938824 const type = getTypeOfSymbol(localOrExportSymbol);
8794- if (!(localOrExportSymbol.flags & SymbolFlags.Variable) || isAssignmentTarget(node)) {
8825+ const declaration = localOrExportSymbol.valueDeclaration;
8826+ // We only narrow variables and parameters occurring in a non-assignment position. For all other
8827+ // entities we simply return the declared type.
8828+ if (!(localOrExportSymbol.flags & SymbolFlags.Variable) || isAssignmentTarget(node) || !declaration) {
87958829 return type;
87968830 }
8797- const declaration = localOrExportSymbol.valueDeclaration;
8798- const includeOuterFunctions = isReadonlySymbol(localOrExportSymbol);
8799- const assumeInitialized = !strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || !declaration ||
8800- getRootDeclaration(declaration).kind === SyntaxKind.Parameter || isInAmbientContext(declaration) ||
8801- !isDeclarationIncludedInFlow(node, declaration, includeOuterFunctions);
8802- const flowType = getFlowTypeOfReference(node, type, assumeInitialized, includeOuterFunctions);
8831+ // The declaration container is the innermost function that encloses the declaration of the variable
8832+ // or parameter. The flow container is the innermost function starting with which we analyze the control
8833+ // flow graph to determine the control flow based type.
8834+ const isParameter = getRootDeclaration(declaration).kind === SyntaxKind.Parameter;
8835+ const declarationContainer = getControlFlowContainer(declaration);
8836+ let flowContainer = getControlFlowContainer(node);
8837+ // When the control flow originates in a function expression or arrow function and we are referencing
8838+ // a const variable or parameter from an outer function, we extend the origin of the control flow
8839+ // analysis to include the immediately enclosing function.
8840+ while (flowContainer !== declarationContainer &&
8841+ (flowContainer.kind === SyntaxKind.FunctionExpression || flowContainer.kind === SyntaxKind.ArrowFunction) &&
8842+ (isReadonlySymbol(localOrExportSymbol) || isParameter && !isParameterAssigned(localOrExportSymbol))) {
8843+ flowContainer = getControlFlowContainer(flowContainer);
8844+ }
8845+ // We only look for uninitialized variables in strict null checking mode, and only when we can analyze
8846+ // the entire control flow graph from the variable's declaration (i.e. when the flow container and
8847+ // declaration container are the same).
8848+ const assumeInitialized = !strictNullChecks || (type.flags & TypeFlags.Any) !== 0 || isParameter ||
8849+ flowContainer !== declarationContainer || isInAmbientContext(declaration);
8850+ const flowType = getFlowTypeOfReference(node, type, assumeInitialized, flowContainer);
8851+ // A variable is considered uninitialized when it is possible to analyze the entire control flow graph
8852+ // from declaration to use, and when the variable's declared type doesn't include undefined but the
8853+ // control flow based type does include undefined.
88038854 if (!assumeInitialized && !(getFalsyFlags(type) & TypeFlags.Undefined) && getFalsyFlags(flowType) & TypeFlags.Undefined) {
88048855 error(node, Diagnostics.Variable_0_is_used_before_being_assigned, symbolToString(symbol));
88058856 // Return the declared type to reduce follow-on errors
@@ -9052,7 +9103,7 @@ namespace ts {
90529103 if (isClassLike(container.parent)) {
90539104 const symbol = getSymbolOfNode(container.parent);
90549105 const type = container.flags & NodeFlags.Static ? getTypeOfSymbol(symbol) : (<InterfaceType>getDeclaredTypeOfSymbol(symbol)).thisType;
9055- return getFlowTypeOfReference(node, type, /*assumeInitialized*/ true, /*includeOuterFunctions */ true );
9106+ return getFlowTypeOfReference(node, type, /*assumeInitialized*/ true, /*flowContainer */ undefined );
90569107 }
90579108
90589109 if (isInJavaScriptFile(node)) {
@@ -10717,7 +10768,7 @@ namespace ts {
1071710768 !(prop.flags & SymbolFlags.Method && propType.flags & TypeFlags.Union)) {
1071810769 return propType;
1071910770 }
10720- return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true, /*includeOuterFunctions */ false );
10771+ return getFlowTypeOfReference(node, propType, /*assumeInitialized*/ true, /*flowContainer */ undefined );
1072110772 }
1072210773
1072310774 function isValidPropertyAccess(node: PropertyAccessExpression | QualifiedName, propertyName: string): boolean {
0 commit comments