@@ -469,10 +469,25 @@ namespace ts {
469469 };
470470
471471 const subtypeRelation = createMap<RelationComparisonResult>();
472+ const subtypeRelationErrors = createMap<DiagnosticMessageChain>();
472473 const assignableRelation = createMap<RelationComparisonResult>();
474+ const assignableRelationErrors = createMap<DiagnosticMessageChain>();
473475 const comparableRelation = createMap<RelationComparisonResult>();
476+ const comparableRelationErrors = createMap<DiagnosticMessageChain>();
474477 const identityRelation = createMap<RelationComparisonResult>();
478+ const identityRelationErrors = createMap<DiagnosticMessageChain>();
475479 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+ }
476491
477492 // This is for caching the result of getSymbolDisplayBuilder. Do not access directly.
478493 let _displayBuilder: SymbolDisplayBuilder;
@@ -8918,6 +8933,17 @@ namespace ts {
89188933 * * Ternary.False if they are not related.
89198934 */
89208935 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+ }
89218947 if (source.flags & TypeFlags.StringOrNumberLiteral && source.flags & TypeFlags.FreshLiteral) {
89228948 source = (<LiteralType>source).regularType;
89238949 }
@@ -9028,6 +9054,14 @@ namespace ts {
90289054 }
90299055 reportRelationError(headMessage, source, target);
90309056 }
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+
90319065 return result;
90329066 }
90339067
@@ -9190,6 +9224,24 @@ namespace ts {
91909224 return result;
91919225 }
91929226
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+
91939245 // Determine if possibly recursive types are related. First, check if the result is already available in the global cache.
91949246 // Second, check if we have already started a comparison of the given two types in which case we assume the result to be true.
91959247 // 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 {
92089260 // this is a copy of the normal code! It can probably be merged somehow.
92099261 const src = (source as TypeReference).target;
92109262 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) ;
92129264 if (!maybeReferenceKeys) {
92139265 maybeReferenceKeys = [];
92149266 }
@@ -9223,19 +9275,11 @@ namespace ts {
92239275 maybeReferenceCount++;
92249276 }
92259277
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;
92379282 }
9238-
92399283 if (!maybeKeys) {
92409284 maybeKeys = [];
92419285 sourceStack = [];
0 commit comments