diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4014902d21d7c..987ed00c43af3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13451,6 +13451,7 @@ namespace ts { let visited: Map; let contravariant = false; let propagationType: Type; + let allowComplexConstraintInference = true; inferFromTypes(originalSource, originalTarget); function inferFromTypes(source: Type, target: Type): void { @@ -13628,7 +13629,15 @@ namespace ts { // getApparentType can return _any_ type, since an indexed access or conditional may simplify to any other type. // If that occurs and it doesn't simplify to an object or intersection, we'll need to restart `inferFromTypes` // with the simplified source. - if (apparentSource !== source && !(apparentSource.flags & (TypeFlags.Object | TypeFlags.Intersection))) { + if (apparentSource !== source && allowComplexConstraintInference && !(apparentSource.flags & (TypeFlags.Object | TypeFlags.Intersection))) { + // TODO: The `allowComplexConstraintInference` flag is a hack! This forbids inference from complex constraints within constraints! + // This isn't required algorithmically, but rather is used to lower the memory burden caused by performing inference + // that is _too good_ in projects with complicated constraints (eg, fp-ts). In such cases, if we did not limit ourselves + // here, we might produce more valid inferences for types, causing us to do more checks and perform more instantiations + // (in addition to the extra stack depth here) which, in turn, can push the already close process over its limit. + // TL;DR: If we ever become generally more memory efficienct (or our resource budget ever increases), we should just + // remove this `allowComplexConstraintInference` flag. + allowComplexConstraintInference = false; return inferFromTypes(apparentSource, target); } source = apparentSource;