@@ -819,6 +819,7 @@ namespace ts {
819
819
let flowLoopCount = 0;
820
820
let sharedFlowCount = 0;
821
821
let flowAnalysisDisabled = false;
822
+ let flowInvocationCount = 0;
822
823
823
824
const emptyStringType = getLiteralType("");
824
825
const zeroType = getLiteralType(0);
@@ -834,8 +835,7 @@ namespace ts {
834
835
const symbolLinks: SymbolLinks[] = [];
835
836
const nodeLinks: NodeLinks[] = [];
836
837
const flowLoopCaches: Map<Type>[] = [];
837
- const flowAssignmentKeys: string[] = [];
838
- const flowAssignmentTypes: FlowType[] = [];
838
+ const flowAssignmentTypes: Type[] = [];
839
839
const flowLoopNodes: FlowNode[] = [];
840
840
const flowLoopKeys: string[] = [];
841
841
const flowLoopTypes: Type[][] = [];
@@ -16698,12 +16698,6 @@ namespace ts {
16698
16698
getInitialTypeOfBindingElement(node);
16699
16699
}
16700
16700
16701
- function getInitialOrAssignedType(node: VariableDeclaration | BindingElement | Expression, reference: Node) {
16702
- return getConstraintForLocation(node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement ?
16703
- getInitialType(<VariableDeclaration | BindingElement>node) :
16704
- getAssignedType(node), reference);
16705
- }
16706
-
16707
16701
function isEmptyArrayAssignment(node: VariableDeclaration | BindingElement | Expression) {
16708
16702
return node.kind === SyntaxKind.VariableDeclaration && (<VariableDeclaration>node).initializer &&
16709
16703
isEmptyArrayLiteral((<VariableDeclaration>node).initializer!) ||
@@ -16990,6 +16984,7 @@ namespace ts {
16990
16984
if (!reference.flowNode || !couldBeUninitialized && !(declaredType.flags & TypeFlags.Narrowable)) {
16991
16985
return declaredType;
16992
16986
}
16987
+ flowInvocationCount++;
16993
16988
const sharedFlowStart = sharedFlowCount;
16994
16989
const evolvedType = getTypeFromFlowType(getTypeAtFlowNode(reference.flowNode));
16995
16990
sharedFlowCount = sharedFlowStart;
@@ -17022,15 +17017,6 @@ namespace ts {
17022
17017
flowDepth++;
17023
17018
while (true) {
17024
17019
const flags = flow.flags;
17025
- if (flags & FlowFlags.Cached) {
17026
- const key = getOrSetCacheKey();
17027
- if (key) {
17028
- const id = getFlowNodeId(flow);
17029
- if (flowAssignmentKeys[id] === key) {
17030
- return flowAssignmentTypes[id];
17031
- }
17032
- }
17033
- }
17034
17020
if (flags & FlowFlags.Shared) {
17035
17021
// We cache results of flow type resolution for shared nodes that were previously visited in
17036
17022
// the same getFlowTypeOfReference invocation. A node is considered shared when it is the
@@ -17061,15 +17047,6 @@ namespace ts {
17061
17047
flow = (<FlowAssignment>flow).antecedent;
17062
17048
continue;
17063
17049
}
17064
- else if (flowLoopCount === flowLoopStart) { // Only cache assignments when not within loop analysis
17065
- const key = getOrSetCacheKey();
17066
- if (key && !isIncomplete(type)) {
17067
- flow.flags |= FlowFlags.Cached;
17068
- const id = getFlowNodeId(flow);
17069
- flowAssignmentKeys[id] = key;
17070
- flowAssignmentTypes[id] = type;
17071
- }
17072
- }
17073
17050
}
17074
17051
else if (flags & FlowFlags.Condition) {
17075
17052
type = getTypeAtFlowCondition(<FlowCondition>flow);
@@ -17122,6 +17099,27 @@ namespace ts {
17122
17099
}
17123
17100
}
17124
17101
17102
+ function getInitialOrAssignedType(flow: FlowAssignment) {
17103
+ const node = flow.node;
17104
+ if (flow.flags & FlowFlags.Cached) {
17105
+ const cached = flowAssignmentTypes[getNodeId(node)];
17106
+ if (cached) {
17107
+ return cached;
17108
+ }
17109
+ }
17110
+ const startInvocationCount = flowInvocationCount;
17111
+ const type = getConstraintForLocation(node.kind === SyntaxKind.VariableDeclaration || node.kind === SyntaxKind.BindingElement ?
17112
+ getInitialType(<VariableDeclaration | BindingElement>node) :
17113
+ getAssignedType(node), reference);
17114
+ // We cache the assigned type when getFlowTypeOfReference was recursively invoked in the
17115
+ // resolution of the assigned type and we're not within loop analysis.
17116
+ if (flowInvocationCount !== startInvocationCount && flowLoopCount === flowLoopStart) {
17117
+ flow.flags |= FlowFlags.Cached;
17118
+ flowAssignmentTypes[getNodeId(node)] = type;
17119
+ }
17120
+ return type;
17121
+ }
17122
+
17125
17123
function getTypeAtFlowAssignment(flow: FlowAssignment) {
17126
17124
const node = flow.node;
17127
17125
// Assignments only narrow the computed type if the declared type is a union type. Thus, we
@@ -17135,11 +17133,11 @@ namespace ts {
17135
17133
if (isEmptyArrayAssignment(node)) {
17136
17134
return getEvolvingArrayType(neverType);
17137
17135
}
17138
- const assignedType = getBaseTypeOfLiteralType(getInitialOrAssignedType(node, reference ));
17136
+ const assignedType = getBaseTypeOfLiteralType(getInitialOrAssignedType(flow ));
17139
17137
return isTypeAssignableTo(assignedType, declaredType) ? assignedType : anyArrayType;
17140
17138
}
17141
17139
if (declaredType.flags & TypeFlags.Union) {
17142
- return getAssignmentReducedType(<UnionType>declaredType, getInitialOrAssignedType(node, reference ));
17140
+ return getAssignmentReducedType(<UnionType>declaredType, getInitialOrAssignedType(flow ));
17143
17141
}
17144
17142
return declaredType;
17145
17143
}
@@ -17309,24 +17307,31 @@ namespace ts {
17309
17307
const antecedentTypes: Type[] = [];
17310
17308
let subtypeReduction = false;
17311
17309
let firstAntecedentType: FlowType | undefined;
17312
- flowLoopNodes[flowLoopCount] = flow;
17313
- flowLoopKeys[flowLoopCount] = key;
17314
- flowLoopTypes[flowLoopCount] = antecedentTypes;
17315
17310
for (const antecedent of flow.antecedents!) {
17316
- flowLoopCount++;
17317
- const flowType = getTypeAtFlowNode(antecedent);
17318
- flowLoopCount--;
17311
+ let flowType;
17319
17312
if (!firstAntecedentType) {
17320
- firstAntecedentType = flowType;
17313
+ // The first antecedent of a loop junction is always the non-looping control
17314
+ // flow path that leads to the top.
17315
+ flowType = firstAntecedentType = getTypeAtFlowNode(antecedent);
17321
17316
}
17322
- const type = getTypeFromFlowType(flowType);
17323
- // If we see a value appear in the cache it is a sign that control flow analysis
17324
- // was restarted and completed by checkExpressionCached. We can simply pick up
17325
- // the resulting type and bail out.
17326
- const cached = cache.get(key);
17327
- if (cached) {
17328
- return cached;
17317
+ else {
17318
+ // All but the first antecedent are the looping control flow paths that lead
17319
+ // back to the loop junction. We track these on the flow loop stack.
17320
+ flowLoopNodes[flowLoopCount] = flow;
17321
+ flowLoopKeys[flowLoopCount] = key;
17322
+ flowLoopTypes[flowLoopCount] = antecedentTypes;
17323
+ flowLoopCount++;
17324
+ flowType = getTypeAtFlowNode(antecedent);
17325
+ flowLoopCount--;
17326
+ // If we see a value appear in the cache it is a sign that control flow analysis
17327
+ // was restarted and completed by checkExpressionCached. We can simply pick up
17328
+ // the resulting type and bail out.
17329
+ const cached = cache.get(key);
17330
+ if (cached) {
17331
+ return cached;
17332
+ }
17329
17333
}
17334
+ const type = getTypeFromFlowType(flowType);
17330
17335
pushIfUnique(antecedentTypes, type);
17331
17336
// If an antecedent type is not a subset of the declared type, we need to perform
17332
17337
// subtype reduction. This happens when a "foreign" type is injected into the control
0 commit comments