@@ -215,7 +215,7 @@ namespace ts {
215215 const flowLoopKeys: string[] = [];
216216 const flowLoopTypes: Type[][] = [];
217217 const visitedFlowNodes: FlowNode[] = [];
218- const visitedFlowTypes: Type [] = [];
218+ const visitedFlowTypes: FlowType [] = [];
219219 const potentialThisCollisions: Node[] = [];
220220 const awaitedTypeStack: number[] = [];
221221
@@ -8090,21 +8090,33 @@ namespace ts {
80908090 f(type) ? type : neverType;
80918091 }
80928092
8093+ function isIncomplete(flowType: FlowType) {
8094+ return flowType.flags === 0;
8095+ }
8096+
8097+ function getTypeFromFlowType(flowType: FlowType) {
8098+ return flowType.flags === 0 ? (<IncompleteType>flowType).type : <Type>flowType;
8099+ }
8100+
8101+ function createFlowType(type: Type, incomplete: boolean): FlowType {
8102+ return incomplete ? { flags: 0, type } : type;
8103+ }
8104+
80938105 function getFlowTypeOfReference(reference: Node, declaredType: Type, assumeInitialized: boolean, includeOuterFunctions: boolean) {
80948106 let key: string;
80958107 if (!reference.flowNode || assumeInitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
80968108 return declaredType;
80978109 }
80988110 const initialType = assumeInitialized ? declaredType : includeFalsyTypes(declaredType, TypeFlags.Undefined);
80998111 const visitedFlowStart = visitedFlowCount;
8100- const result = getTypeAtFlowNode(reference.flowNode);
8112+ const result = getTypeFromFlowType( getTypeAtFlowNode(reference.flowNode) );
81018113 visitedFlowCount = visitedFlowStart;
81028114 if (reference.parent.kind === SyntaxKind.NonNullExpression && getTypeWithFacts(result, TypeFacts.NEUndefinedOrNull) === neverType) {
81038115 return declaredType;
81048116 }
81058117 return result;
81068118
8107- function getTypeAtFlowNode(flow: FlowNode): Type {
8119+ function getTypeAtFlowNode(flow: FlowNode): FlowType {
81088120 while (true) {
81098121 if (flow.flags & FlowFlags.Shared) {
81108122 // We cache results of flow type resolution for shared nodes that were previously visited in
@@ -8116,7 +8128,7 @@ namespace ts {
81168128 }
81178129 }
81188130 }
8119- let type: Type ;
8131+ let type: FlowType ;
81208132 if (flow.flags & FlowFlags.Assignment) {
81218133 type = getTypeAtFlowAssignment(<FlowAssignment>flow);
81228134 if (!type) {
@@ -8184,41 +8196,44 @@ namespace ts {
81848196 return undefined;
81858197 }
81868198
8187- function getTypeAtFlowCondition(flow: FlowCondition) {
8188- let type = getTypeAtFlowNode(flow.antecedent);
8199+ function getTypeAtFlowCondition(flow: FlowCondition): FlowType {
8200+ const flowType = getTypeAtFlowNode(flow.antecedent);
8201+ let type = getTypeFromFlowType(flowType);
81898202 if (type !== neverType) {
81908203 // If we have an antecedent type (meaning we're reachable in some way), we first
8191- // attempt to narrow the antecedent type. If that produces the nothing type, then
8192- // we take the type guard as an indication that control could reach here in a
8193- // manner not understood by the control flow analyzer (e.g. a function argument
8194- // has an invalid type, or a nested function has possibly made an assignment to a
8195- // captured variable). We proceed by reverting to the declared type and then
8204+ // attempt to narrow the antecedent type. If that produces the never type, and if
8205+ // the antecedent type is incomplete (i.e. a transient type in a loop), then we
8206+ // take the type guard as an indication that control *could* reach here once we
8207+ // have the complete type. We proceed by reverting to the declared type and then
81968208 // narrow that.
81978209 const assumeTrue = (flow.flags & FlowFlags.TrueCondition) !== 0;
81988210 type = narrowType(type, flow.expression, assumeTrue);
8199- if (type === neverType) {
8211+ if (type === neverType && isIncomplete(flowType) ) {
82008212 type = narrowType(declaredType, flow.expression, assumeTrue);
82018213 }
82028214 }
8203- return type;
8215+ return createFlowType( type, isIncomplete(flowType)) ;
82048216 }
82058217
8206- function getTypeAtSwitchClause(flow: FlowSwitchClause) {
8207- const type = getTypeAtFlowNode(flow.antecedent);
8218+ function getTypeAtSwitchClause(flow: FlowSwitchClause): FlowType {
8219+ const flowType = getTypeAtFlowNode(flow.antecedent);
8220+ let type = getTypeFromFlowType(flowType);
82088221 const expr = flow.switchStatement.expression;
82098222 if (isMatchingReference(reference, expr)) {
8210- return narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd);
8223+ type = narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd);
82118224 }
8212- if (isMatchingPropertyAccess(expr)) {
8213- return narrowTypeByDiscriminant(type, <PropertyAccessExpression>expr, t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd));
8225+ else if (isMatchingPropertyAccess(expr)) {
8226+ type = narrowTypeByDiscriminant(type, <PropertyAccessExpression>expr, t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd));
82148227 }
8215- return type;
8228+ return createFlowType( type, isIncomplete(flowType)) ;
82168229 }
82178230
8218- function getTypeAtFlowBranchLabel(flow: FlowLabel) {
8231+ function getTypeAtFlowBranchLabel(flow: FlowLabel): FlowType {
82198232 const antecedentTypes: Type[] = [];
8233+ let seenIncomplete = false;
82208234 for (const antecedent of flow.antecedents) {
8221- const type = getTypeAtFlowNode(antecedent);
8235+ const flowType = getTypeAtFlowNode(antecedent);
8236+ const type = getTypeFromFlowType(flowType);
82228237 // If the type at a particular antecedent path is the declared type and the
82238238 // reference is known to always be assigned (i.e. when declared and initial types
82248239 // are the same), there is no reason to process more antecedents since the only
@@ -8229,11 +8244,14 @@ namespace ts {
82298244 if (!contains(antecedentTypes, type)) {
82308245 antecedentTypes.push(type);
82318246 }
8247+ if (isIncomplete(flowType)) {
8248+ seenIncomplete = true;
8249+ }
82328250 }
8233- return getUnionType(antecedentTypes);
8251+ return createFlowType( getUnionType(antecedentTypes), seenIncomplete );
82348252 }
82358253
8236- function getTypeAtFlowLoopLabel(flow: FlowLabel) {
8254+ function getTypeAtFlowLoopLabel(flow: FlowLabel): FlowType {
82378255 // If we have previously computed the control flow type for the reference at
82388256 // this flow loop junction, return the cached type.
82398257 const id = getFlowNodeId(flow);
@@ -8245,12 +8263,12 @@ namespace ts {
82458263 return cache[key];
82468264 }
82478265 // If this flow loop junction and reference are already being processed, return
8248- // the union of the types computed for each branch so far. We should never see
8249- // an empty array here because the first antecedent of a loop junction is always
8250- // the non-looping control flow path that leads to the top.
8266+ // the union of the types computed for each branch so far, marked as incomplete.
8267+ // We should never see an empty array here because the first antecedent of a loop
8268+ // junction is always the non-looping control flow path that leads to the top.
82518269 for (let i = flowLoopStart; i < flowLoopCount; i++) {
82528270 if (flowLoopNodes[i] === flow && flowLoopKeys[i] === key) {
8253- return getUnionType(flowLoopTypes[i]);
8271+ return createFlowType( getUnionType(flowLoopTypes[i]), /*incomplete*/ true );
82548272 }
82558273 }
82568274 // Add the flow loop junction and reference to the in-process stack and analyze
@@ -8261,7 +8279,7 @@ namespace ts {
82618279 flowLoopTypes[flowLoopCount] = antecedentTypes;
82628280 for (const antecedent of flow.antecedents) {
82638281 flowLoopCount++;
8264- const type = getTypeAtFlowNode(antecedent);
8282+ const type = getTypeFromFlowType( getTypeAtFlowNode(antecedent) );
82658283 flowLoopCount--;
82668284 // If we see a value appear in the cache it is a sign that control flow analysis
82678285 // was restarted and completed by checkExpressionCached. We can simply pick up
0 commit comments