From 7698260af81cd800109c507dc7ab782a5029e8cb Mon Sep 17 00:00:00 2001 From: Wesley Wigham Date: Mon, 5 Jun 2023 11:38:57 -0700 Subject: [PATCH] Conditionalize the carrying-forward of type variable containment checks within `getObjectTypeInstantiation` more. The existing check pulls the flags forward onto primitives/unions/intersections which may result from `instantiateMappedType` unconditionally. This means we may mark unions and intersections as possibly containing type parameters when they in fact cannot. --- src/compiler/checker.ts | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 72d7f692f9b43..4eb2db0bd29e8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -5584,7 +5584,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function createIntrinsicType(kind: TypeFlags, intrinsicName: string, objectFlags = ObjectFlags.None): IntrinsicType { const type = createType(kind) as IntrinsicType; type.intrinsicName = intrinsicName; - type.objectFlags = objectFlags; + type.objectFlags = objectFlags | ObjectFlags.CouldContainTypeVariablesComputed | ObjectFlags.IsGenericTypeComputed | ObjectFlags.IsUnknownLikeUnionComputed | ObjectFlags.IsNeverIntersectionComputed; return type; } @@ -18797,13 +18797,23 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { result = target.objectFlags & ObjectFlags.Reference ? createDeferredTypeReference((type as DeferredTypeReference).target, (type as DeferredTypeReference).node, newMapper, newAliasSymbol, newAliasTypeArguments) : target.objectFlags & ObjectFlags.Mapped ? instantiateMappedType(target as MappedType, newMapper, newAliasSymbol, newAliasTypeArguments) : instantiateAnonymousType(target, newMapper, newAliasSymbol, newAliasTypeArguments); - // If none of the type arguments for the outer type parameters contain type variables, it follows - // that the instantiated type doesn't reference type variables. - if (result.flags & TypeFlags.ObjectFlagsType && !((result as ObjectFlagsType).objectFlags & ObjectFlags.CouldContainTypeVariablesComputed)) { - (result as ObjectFlagsType).objectFlags |= ObjectFlags.CouldContainTypeVariablesComputed | - (some(typeArguments, couldContainTypeVariables) ? ObjectFlags.CouldContainTypeVariables : 0); + target.instantiations.set(id, result); // Set cached result early in case we recursively invoke instantiation while eagerly computing type variable visibility below + const resultObjectFlags = getObjectFlags(result); + if (result.flags & TypeFlags.ObjectFlagsType && !(resultObjectFlags & ObjectFlags.CouldContainTypeVariablesComputed)) { + const resultCouldContainTypeVariables = some(typeArguments, couldContainTypeVariables); // one of the input type arguments might be or contain the result + if (!(getObjectFlags(result) & ObjectFlags.CouldContainTypeVariablesComputed)) { + // if `result` is one of the object types we tried to make (it may not be, due to how `instantiateMappedType` works), we can carry forward the type variable containment check from the input type arguments + if (resultObjectFlags & (ObjectFlags.Mapped | ObjectFlags.Anonymous | ObjectFlags.Reference)) { + (result as ObjectFlagsType).objectFlags |= ObjectFlags.CouldContainTypeVariablesComputed | (resultCouldContainTypeVariables ? ObjectFlags.CouldContainTypeVariables : 0); + } + // If none of the type arguments for the outer type parameters contain type variables, it follows + // that the instantiated type doesn't reference type variables. + // Intrinsics have `CouldContainTypeVariablesComputed` pre-set, so this should only cover unions and intersections resulting from `instantiateMappedType` + else { + (result as ObjectFlagsType).objectFlags |= !resultCouldContainTypeVariables ? ObjectFlags.CouldContainTypeVariablesComputed : 0; + } + } } - target.instantiations.set(id, result); } return result; }