@@ -12889,65 +12889,82 @@ namespace ts {
12889
12889
}
12890
12890
12891
12891
function getConditionalType(root: ConditionalRoot, mapper: TypeMapper | undefined): Type {
12892
- const checkType = instantiateType(root.checkType, mapper);
12893
- const extendsType = instantiateType(root.extendsType, mapper);
12894
- if (checkType === wildcardType || extendsType === wildcardType) {
12895
- return wildcardType;
12892
+ let result;
12893
+ let extraTypes: Type[] | undefined;
12894
+ // We loop here for an immediately nested conditional type in the false position, effectively treating
12895
+ // types of the form 'A extends B ? X : C extends D ? Y : E extends F ? Z : ...' as a single construct for
12896
+ // purposes of resolution. This means such types aren't subject to the instatiation depth limiter.
12897
+ while (true) {
12898
+ const checkType = instantiateType(root.checkType, mapper);
12899
+ const checkTypeInstantiable = isGenericObjectType(checkType) || isGenericIndexType(checkType);
12900
+ const extendsType = instantiateType(root.extendsType, mapper);
12901
+ if (checkType === wildcardType || extendsType === wildcardType) {
12902
+ return wildcardType;
12903
+ }
12904
+ let combinedMapper: TypeMapper | undefined;
12905
+ if (root.inferTypeParameters) {
12906
+ const context = createInferenceContext(root.inferTypeParameters, /*signature*/ undefined, InferenceFlags.None);
12907
+ // We skip inference of the possible `infer` types unles the `extendsType` _is_ an infer type
12908
+ // if it was, it's trivial to say that extendsType = checkType, however such a pattern is used to
12909
+ // "reset" the type being build up during constraint calculation and avoid making an apparently "infinite" constraint
12910
+ // so in those cases we refain from performing inference and retain the uninfered type parameter
12911
+ if (!checkTypeInstantiable || !some(root.inferTypeParameters, t => t === extendsType)) {
12912
+ // We don't want inferences from constraints as they may cause us to eagerly resolve the
12913
+ // conditional type instead of deferring resolution. Also, we always want strict function
12914
+ // types rules (i.e. proper contravariance) for inferences.
12915
+ inferTypes(context.inferences, checkType, extendsType, InferencePriority.NoConstraints | InferencePriority.AlwaysStrict);
12916
+ }
12917
+ combinedMapper = mergeTypeMappers(mapper, context.mapper);
12918
+ }
12919
+ // Instantiate the extends type including inferences for 'infer T' type parameters
12920
+ const inferredExtendsType = combinedMapper ? instantiateType(root.extendsType, combinedMapper) : extendsType;
12921
+ // We attempt to resolve the conditional type only when the check and extends types are non-generic
12922
+ if (!checkTypeInstantiable && !isGenericObjectType(inferredExtendsType) && !isGenericIndexType(inferredExtendsType)) {
12923
+ // Return falseType for a definitely false extends check. We check an instantiations of the two
12924
+ // types with type parameters mapped to the wildcard type, the most permissive instantiations
12925
+ // possible (the wildcard type is assignable to and from all types). If those are not related,
12926
+ // then no instantiations will be and we can just return the false branch type.
12927
+ if (!(inferredExtendsType.flags & TypeFlags.AnyOrUnknown) && (checkType.flags & TypeFlags.Any || !isTypeAssignableTo(getPermissiveInstantiation(checkType), getPermissiveInstantiation(inferredExtendsType)))) {
12928
+ // Return union of trueType and falseType for 'any' since it matches anything
12929
+ if (checkType.flags & TypeFlags.Any) {
12930
+ (extraTypes || (extraTypes = [])).push(instantiateTypeWithoutDepthIncrease(root.trueType, combinedMapper || mapper));
12931
+ }
12932
+ // If falseType is an immediately nested conditional type that isn't distributive or has an
12933
+ // identical checkType, switch to that type and loop.
12934
+ const falseType = root.falseType;
12935
+ if (falseType.flags & TypeFlags.Conditional) {
12936
+ const newRoot = (<ConditionalType>falseType).root;
12937
+ if (newRoot.node.parent === root.node && (!newRoot.isDistributive || newRoot.checkType === root.checkType)) {
12938
+ root = newRoot;
12939
+ continue;
12940
+ }
12941
+ }
12942
+ result = instantiateTypeWithoutDepthIncrease(falseType, mapper);
12943
+ break;
12944
+ }
12945
+ // Return trueType for a definitely true extends check. We check instantiations of the two
12946
+ // types with type parameters mapped to their restrictive form, i.e. a form of the type parameter
12947
+ // that has no constraint. This ensures that, for example, the type
12948
+ // type Foo<T extends { x: any }> = T extends { x: string } ? string : number
12949
+ // doesn't immediately resolve to 'string' instead of being deferred.
12950
+ if (inferredExtendsType.flags & TypeFlags.AnyOrUnknown || isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(inferredExtendsType))) {
12951
+ result = instantiateTypeWithoutDepthIncrease(root.trueType, combinedMapper || mapper);
12952
+ break;
12953
+ }
12954
+ }
12955
+ // Return a deferred type for a check that is neither definitely true nor definitely false
12956
+ const erasedCheckType = getActualTypeVariable(checkType);
12957
+ result = <ConditionalType>createType(TypeFlags.Conditional);
12958
+ result.root = root;
12959
+ result.checkType = erasedCheckType;
12960
+ result.extendsType = extendsType;
12961
+ result.mapper = mapper;
12962
+ result.combinedMapper = combinedMapper;
12963
+ result.aliasSymbol = root.aliasSymbol;
12964
+ result.aliasTypeArguments = instantiateTypes(root.aliasTypeArguments, mapper!); // TODO: GH#18217
12965
+ break;
12896
12966
}
12897
- const checkTypeInstantiable = isGenericObjectType(checkType) || isGenericIndexType(checkType);
12898
- let combinedMapper: TypeMapper | undefined;
12899
- if (root.inferTypeParameters) {
12900
- const context = createInferenceContext(root.inferTypeParameters, /*signature*/ undefined, InferenceFlags.None);
12901
- // We skip inference of the possible `infer` types unles the `extendsType` _is_ an infer type
12902
- // if it was, it's trivial to say that extendsType = checkType, however such a pattern is used to
12903
- // "reset" the type being build up during constraint calculation and avoid making an apparently "infinite" constraint
12904
- // so in those cases we refain from performing inference and retain the uninfered type parameter
12905
- if (!checkTypeInstantiable || !some(root.inferTypeParameters, t => t === extendsType)) {
12906
- // We don't want inferences from constraints as they may cause us to eagerly resolve the
12907
- // conditional type instead of deferring resolution. Also, we always want strict function
12908
- // types rules (i.e. proper contravariance) for inferences.
12909
- inferTypes(context.inferences, checkType, extendsType, InferencePriority.NoConstraints | InferencePriority.AlwaysStrict);
12910
- }
12911
- combinedMapper = mergeTypeMappers(mapper, context.mapper);
12912
- }
12913
- // Instantiate the extends type including inferences for 'infer T' type parameters
12914
- const inferredExtendsType = combinedMapper ? instantiateType(root.extendsType, combinedMapper) : extendsType;
12915
- // We attempt to resolve the conditional type only when the check and extends types are non-generic
12916
- if (!checkTypeInstantiable && !isGenericObjectType(inferredExtendsType) && !isGenericIndexType(inferredExtendsType)) {
12917
- if (inferredExtendsType.flags & TypeFlags.AnyOrUnknown) {
12918
- return instantiateType(root.trueType, combinedMapper || mapper);
12919
- }
12920
- // Return union of trueType and falseType for 'any' since it matches anything
12921
- if (checkType.flags & TypeFlags.Any) {
12922
- return getUnionType([instantiateType(root.trueType, combinedMapper || mapper), instantiateType(root.falseType, mapper)]);
12923
- }
12924
- // Return falseType for a definitely false extends check. We check an instantiations of the two
12925
- // types with type parameters mapped to the wildcard type, the most permissive instantiations
12926
- // possible (the wildcard type is assignable to and from all types). If those are not related,
12927
- // then no instantiations will be and we can just return the false branch type.
12928
- if (!isTypeAssignableTo(getPermissiveInstantiation(checkType), getPermissiveInstantiation(inferredExtendsType))) {
12929
- return instantiateType(root.falseType, mapper);
12930
- }
12931
- // Return trueType for a definitely true extends check. We check instantiations of the two
12932
- // types with type parameters mapped to their restrictive form, i.e. a form of the type parameter
12933
- // that has no constraint. This ensures that, for example, the type
12934
- // type Foo<T extends { x: any }> = T extends { x: string } ? string : number
12935
- // doesn't immediately resolve to 'string' instead of being deferred.
12936
- if (isTypeAssignableTo(getRestrictiveInstantiation(checkType), getRestrictiveInstantiation(inferredExtendsType))) {
12937
- return instantiateType(root.trueType, combinedMapper || mapper);
12938
- }
12939
- }
12940
- // Return a deferred type for a check that is neither definitely true nor definitely false
12941
- const erasedCheckType = getActualTypeVariable(checkType);
12942
- const result = <ConditionalType>createType(TypeFlags.Conditional);
12943
- result.root = root;
12944
- result.checkType = erasedCheckType;
12945
- result.extendsType = extendsType;
12946
- result.mapper = mapper;
12947
- result.combinedMapper = combinedMapper;
12948
- result.aliasSymbol = root.aliasSymbol;
12949
- result.aliasTypeArguments = instantiateTypes(root.aliasTypeArguments, mapper!); // TODO: GH#18217
12950
- return result;
12967
+ return extraTypes ? getUnionType(append(extraTypes, result)) : result;
12951
12968
}
12952
12969
12953
12970
function getTrueTypeFromConditionalType(type: ConditionalType) {
@@ -13929,6 +13946,17 @@ namespace ts {
13929
13946
return result;
13930
13947
}
13931
13948
13949
+ /**
13950
+ * This can be used to avoid the penalty on instantiation depth for types which result from immediate
13951
+ * simplification. It essentially removes the depth increase done in `instantiateType`.
13952
+ */
13953
+ function instantiateTypeWithoutDepthIncrease(type: Type, mapper: TypeMapper | undefined) {
13954
+ instantiationDepth--;
13955
+ const result = instantiateType(type, mapper);
13956
+ instantiationDepth++;
13957
+ return result;
13958
+ }
13959
+
13932
13960
function instantiateTypeWorker(type: Type, mapper: TypeMapper): Type {
13933
13961
const flags = type.flags;
13934
13962
if (flags & TypeFlags.TypeParameter) {
0 commit comments