From 1b13408834510d0ed3944eb39265645f36a86d46 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sun, 22 Jun 2025 16:08:02 -0400 Subject: [PATCH] Improve contextual types inferred from return types --- src/compiler/checker.ts | 6 ++++-- src/compiler/types.ts | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 76e81a95979..5d37db197b8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -26727,7 +26727,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } if (!inference.isFixed) { const candidate = propagationType || source; - if (candidate === blockedStringType) { + // ReturnMapper inferences are used only for contextual types. We exclude `any` and `unknown` types + // that might otherwise swallow other useful inferences. + if (candidate === blockedStringType || candidate.flags & TypeFlags.AnyOrUnknown && priority & InferencePriority.ReturnMapper) { return; } if (inference.priority === undefined || priority < inference.priority) { @@ -35698,7 +35700,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // type parameters for which the inferences are being made. const returnContext = createInferenceContext(signature.typeParameters!, signature, context.flags); const returnSourceType = instantiateType(contextualType, outerContext && createOuterReturnMapper(outerContext)); - inferTypes(returnContext.inferences, returnSourceType, inferenceTargetType); + inferTypes(returnContext.inferences, returnSourceType, inferenceTargetType, InferencePriority.ReturnMapper); context.returnMapper = some(returnContext.inferences, hasInferenceCandidates) ? getMapperFromContext(cloneInferredPartOfContext(returnContext)) : undefined; } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index ee7f4338a3a..fd03eb7be5d 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -7103,9 +7103,10 @@ export const enum InferencePriority { LiteralKeyof = 1 << 8, // Inference made from a string literal to a keyof T NoConstraints = 1 << 9, // Don't infer from constraints of instantiable types AlwaysStrict = 1 << 10, // Always use strict rules for contravariant inferences - MaxValue = 1 << 11, // Seed for inference priority tracking + ReturnMapper = 1 << 11, // Inferring for return mapper + MaxValue = 1 << 12, // Seed for inference priority tracking - PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates + PriorityImpliesCombination = ReturnType | ReturnMapper | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates Circularity = -1, // Inference circularity (value less than all other priorities) } @@ -7129,6 +7130,7 @@ export const enum InferenceFlags { NoDefault = 1 << 0, // Infer silentNeverType for no inferences (otherwise anyType or unknownType) AnyDefault = 1 << 1, // Infer anyType (in JS files) for no inferences (otherwise unknownType) SkippedGenericFunction = 1 << 2, // A generic function was skipped during inference + NoUnknownInference = 1 << 3, } /**