@@ -559,6 +559,8 @@ namespace ts {
559
559
const symbolLinks: SymbolLinks[] = [];
560
560
const nodeLinks: NodeLinks[] = [];
561
561
const flowLoopCaches: Map<Type>[] = [];
562
+ const flowAssignmentKeys: string[] = [];
563
+ const flowAssignmentTypes: FlowType[] = [];
562
564
const flowLoopNodes: FlowNode[] = [];
563
565
const flowLoopKeys: string[] = [];
564
566
const flowLoopTypes: Type[][] = [];
@@ -15666,21 +15668,21 @@ namespace ts {
15666
15668
// The result is undefined if the reference isn't a dotted name. We prefix nodes
15667
15669
// occurring in an apparent type position with '@' because the control flow type
15668
15670
// of such nodes may be based on the apparent type instead of the declared type.
15669
- function getFlowCacheKey(node: Node): string | undefined {
15671
+ function getFlowCacheKey(node: Node, declaredType: Type, initialType: Type, flowContainer: Node | undefined ): string | undefined {
15670
15672
switch (node.kind) {
15671
15673
case SyntaxKind.Identifier:
15672
15674
const symbol = getResolvedSymbol(<Identifier>node);
15673
- return symbol !== unknownSymbol ? ( isConstraintPosition(node) ? "@" : "") + getSymbolId(symbol) : undefined;
15675
+ return symbol !== unknownSymbol ? `${flowContainer ? getNodeId(flowContainer) : "-1"}|${getTypeId(declaredType)}|${getTypeId(initialType)}|${ isConstraintPosition(node) ? "@" : ""}${ getSymbolId(symbol)}` : undefined;
15674
15676
case SyntaxKind.ThisKeyword:
15675
15677
return "0";
15676
15678
case SyntaxKind.NonNullExpression:
15677
15679
case SyntaxKind.ParenthesizedExpression:
15678
- return getFlowCacheKey((<NonNullExpression | ParenthesizedExpression>node).expression);
15680
+ return getFlowCacheKey((<NonNullExpression | ParenthesizedExpression>node).expression, declaredType, initialType, flowContainer );
15679
15681
case SyntaxKind.PropertyAccessExpression:
15680
15682
case SyntaxKind.ElementAccessExpression:
15681
15683
const propName = getAccessedPropertyName(<AccessExpression>node);
15682
15684
if (propName !== undefined) {
15683
- const key = getFlowCacheKey((<AccessExpression>node).expression);
15685
+ const key = getFlowCacheKey((<AccessExpression>node).expression, declaredType, initialType, flowContainer );
15684
15686
return key && key + "." + propName;
15685
15687
}
15686
15688
}
@@ -16345,6 +16347,7 @@ namespace ts {
16345
16347
16346
16348
function getFlowTypeOfReference(reference: Node, declaredType: Type, initialType = declaredType, flowContainer?: Node, couldBeUninitialized?: boolean) {
16347
16349
let key: string | undefined;
16350
+ let keySet = false;
16348
16351
let flowDepth = 0;
16349
16352
if (flowAnalysisDisabled) {
16350
16353
return errorType;
@@ -16365,6 +16368,14 @@ namespace ts {
16365
16368
}
16366
16369
return resultType;
16367
16370
16371
+ function getOrSetCacheKey() {
16372
+ if (keySet) {
16373
+ return key;
16374
+ }
16375
+ keySet = true;
16376
+ return key = getFlowCacheKey(reference, declaredType, initialType, flowContainer);
16377
+ }
16378
+
16368
16379
function getTypeAtFlowNode(flow: FlowNode): FlowType {
16369
16380
if (flowDepth === 2000) {
16370
16381
// We have made 2000 recursive invocations. To avoid overflowing the call stack we report an error
@@ -16376,6 +16387,15 @@ namespace ts {
16376
16387
flowDepth++;
16377
16388
while (true) {
16378
16389
const flags = flow.flags;
16390
+ if (flags & FlowFlags.Cached) {
16391
+ const key = getOrSetCacheKey();
16392
+ if (key) {
16393
+ const id = getFlowNodeId(flow);
16394
+ if (flowAssignmentKeys[id] === key) {
16395
+ return flowAssignmentTypes[id];
16396
+ }
16397
+ }
16398
+ }
16379
16399
if (flags & FlowFlags.Shared) {
16380
16400
// We cache results of flow type resolution for shared nodes that were previously visited in
16381
16401
// the same getFlowTypeOfReference invocation. A node is considered shared when it is the
@@ -16406,6 +16426,15 @@ namespace ts {
16406
16426
flow = (<FlowAssignment>flow).antecedent;
16407
16427
continue;
16408
16428
}
16429
+ else if (flowLoopCount === flowLoopStart) { // Only cache assignments when not within loop analysis
16430
+ const key = getOrSetCacheKey();
16431
+ if (key && !isIncomplete(type)) {
16432
+ flow.flags |= FlowFlags.Cached;
16433
+ const id = getFlowNodeId(flow);
16434
+ flowAssignmentKeys[id] = key;
16435
+ flowAssignmentTypes[id] = type;
16436
+ }
16437
+ }
16409
16438
}
16410
16439
else if (flags & FlowFlags.Condition) {
16411
16440
type = getTypeAtFlowCondition(<FlowCondition>flow);
@@ -16618,12 +16647,10 @@ namespace ts {
16618
16647
// this flow loop junction, return the cached type.
16619
16648
const id = getFlowNodeId(flow);
16620
16649
const cache = flowLoopCaches[id] || (flowLoopCaches[id] = createMap<Type>());
16650
+ const key = getOrSetCacheKey();
16621
16651
if (!key) {
16622
- key = getFlowCacheKey(reference);
16623
16652
// No cache key is generated when binding patterns are in unnarrowable situations
16624
- if (!key) {
16625
- return declaredType;
16626
- }
16653
+ return declaredType;
16627
16654
}
16628
16655
const cached = cache.get(key);
16629
16656
if (cached) {
0 commit comments