@@ -469,10 +469,25 @@ namespace ts {
469
469
};
470
470
471
471
const subtypeRelation = createMap<RelationComparisonResult>();
472
+ const subtypeRelationErrors = createMap<DiagnosticMessageChain>();
472
473
const assignableRelation = createMap<RelationComparisonResult>();
474
+ const assignableRelationErrors = createMap<DiagnosticMessageChain>();
473
475
const comparableRelation = createMap<RelationComparisonResult>();
476
+ const comparableRelationErrors = createMap<DiagnosticMessageChain>();
474
477
const identityRelation = createMap<RelationComparisonResult>();
478
+ const identityRelationErrors = createMap<DiagnosticMessageChain>();
475
479
const enumRelation = createMap<boolean>();
480
+ const enumRelationErrors = createMap<DiagnosticMessageChain>();
481
+
482
+ function getErrorCache(relation: Map<RelationComparisonResult> | Map<boolean>) {
483
+ switch (true) {
484
+ case relation === subtypeRelation: return subtypeRelationErrors;
485
+ case relation === assignableRelation: return assignableRelationErrors;
486
+ case relation === comparableRelation: return comparableRelationErrors;
487
+ case relation === identityRelation: return identityRelationErrors;
488
+ case relation === enumRelation: return enumRelationErrors;
489
+ }
490
+ }
476
491
477
492
// This is for caching the result of getSymbolDisplayBuilder. Do not access directly.
478
493
let _displayBuilder: SymbolDisplayBuilder;
@@ -8918,6 +8933,17 @@ namespace ts {
8918
8933
* * Ternary.False if they are not related.
8919
8934
*/
8920
8935
function isRelatedTo(source: Type, target: Type, reportErrors?: boolean, headMessage?: DiagnosticMessage): Ternary {
8936
+ const comparisonId = comparisonTypeId(source, target);
8937
+ const cachedResult = fetchRelationResult(comparisonId);
8938
+ if (cachedResult !== undefined) {
8939
+ if (reportErrors && cachedResult === Ternary.False) {
8940
+ const cachedChain = getErrorCache(relation).get(comparisonId);
8941
+ if (cachedChain) {
8942
+ errorInfo = concatenateDiagnosticMessageChains(errorInfo, cachedChain);
8943
+ }
8944
+ }
8945
+ return cachedResult;
8946
+ }
8921
8947
if (source.flags & TypeFlags.StringOrNumberLiteral && source.flags & TypeFlags.FreshLiteral) {
8922
8948
source = (<LiteralType>source).regularType;
8923
8949
}
@@ -9028,6 +9054,14 @@ namespace ts {
9028
9054
}
9029
9055
reportRelationError(headMessage, source, target);
9030
9056
}
9057
+
9058
+ if (fetchRelationResult(comparisonId) === undefined) {
9059
+ relation.set(comparisonId, result ? RelationComparisonResult.Succeeded : reportErrors ? RelationComparisonResult.FailedAndReported : RelationComparisonResult.Failed);
9060
+ if (reportErrors && errorInfo) {
9061
+ getErrorCache(relation).set(comparisonId, errorInfo);
9062
+ }
9063
+ }
9064
+
9031
9065
return result;
9032
9066
}
9033
9067
@@ -9190,6 +9224,24 @@ namespace ts {
9190
9224
return result;
9191
9225
}
9192
9226
9227
+ function comparisonTypeId(source: Type, target: Type) {
9228
+ return relation !== identityRelation || source.id < target.id ? source.id + "," + target.id : target.id + "," + source.id;
9229
+ }
9230
+
9231
+ function fetchRelationResult(id: string, reportErrors?: boolean): Ternary | undefined {
9232
+ const related = relation.get(id);
9233
+ if (related !== undefined) {
9234
+ if (reportErrors && related === RelationComparisonResult.Failed) {
9235
+ // We are elaborating errors and the cached result is an unreported failure. Record the result as a reported
9236
+ // failure and continue computing the relation such that errors get reported.
9237
+ relation.set(id, RelationComparisonResult.FailedAndReported);
9238
+ }
9239
+ else {
9240
+ return related === RelationComparisonResult.Succeeded ? Ternary.True : Ternary.False;
9241
+ }
9242
+ }
9243
+ }
9244
+
9193
9245
// Determine if possibly recursive types are related. First, check if the result is already available in the global cache.
9194
9246
// Second, check if we have already started a comparison of the given two types in which case we assume the result to be true.
9195
9247
// Third, check if both types are part of deeply nested chains of generic type instantiations and if so assume the types are
@@ -9208,7 +9260,7 @@ namespace ts {
9208
9260
// this is a copy of the normal code! It can probably be merged somehow.
9209
9261
const src = (source as TypeReference).target;
9210
9262
const trg = (target as TypeReference).target;
9211
- const id = relation !== identityRelation || src.id < trg.id ? src.id + "," + trg.id : trg.id + "," + src.id ;
9263
+ const id = comparisonTypeId( src, trg) ;
9212
9264
if (!maybeReferenceKeys) {
9213
9265
maybeReferenceKeys = [];
9214
9266
}
@@ -9223,19 +9275,11 @@ namespace ts {
9223
9275
maybeReferenceCount++;
9224
9276
}
9225
9277
9226
- const id = relation !== identityRelation || source.id < target.id ? source.id + "," + target.id : target.id + "," + source.id;
9227
- const related = relation.get(id);
9228
- if (related !== undefined) {
9229
- if (reportErrors && related === RelationComparisonResult.Failed) {
9230
- // We are elaborating errors and the cached result is an unreported failure. Record the result as a reported
9231
- // failure and continue computing the relation such that errors get reported.
9232
- relation.set(id, RelationComparisonResult.FailedAndReported);
9233
- }
9234
- else {
9235
- return related === RelationComparisonResult.Succeeded ? Ternary.True : Ternary.False;
9236
- }
9278
+ const id = comparisonTypeId(source, target);
9279
+ const cachedResult = fetchRelationResult(id);
9280
+ if (cachedResult !== undefined) {
9281
+ return cachedResult;
9237
9282
}
9238
-
9239
9283
if (!maybeKeys) {
9240
9284
maybeKeys = [];
9241
9285
sourceStack = [];
0 commit comments