diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 61607e9c90400..e8299a89c4e68 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14898,11 +14898,8 @@ namespace ts { function createReverseMappedType(source: Type, target: MappedType, constraint: IndexType) { // We consider a source type reverse mappable if it has a string index signature or if - // it has one or more properties and all properties have inferable types. - const properties = getPropertiesOfType(source); - const isReverseMappable = getIndexInfoOfType(source, IndexKind.String) || - properties.length !== 0 && every(properties, prop => isPartiallyInferableType(getTypeOfSymbol(prop))); - if (!isReverseMappable) { + // it has one or more properties and is of a partially inferable type. + if (!(getIndexInfoOfType(source, IndexKind.String) || getPropertiesOfType(source).length !== 0 && isPartiallyInferableType(source))) { return undefined; } // For arrays and tuples we infer new arrays and tuples where the reverse mapping has been @@ -15287,7 +15284,11 @@ namespace ts { const inferredType = inferTypeForHomomorphicMappedType(source, target, constraintType); if (inferredType) { const savePriority = priority; - priority |= InferencePriority.HomomorphicMappedType; + // We assign a lower priority to inferences made from types containing non-inferrable + // types because we may only have a partial result (i.e. we may have failed to make + // reverse inferences for some properties). + priority |= getObjectFlags(source) & ObjectFlags.NonInferrableType ? + InferencePriority.PartialHomomorphicMappedType : InferencePriority.HomomorphicMappedType; inferFromTypes(inferredType, inference.typeParameter); priority = savePriority; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5e2d6aba474ee..6c6e585d4920f 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -4405,15 +4405,16 @@ namespace ts { export type TypeMapper = (t: TypeParameter) => Type; export const enum InferencePriority { - NakedTypeVariable = 1 << 0, // Naked type variable in union or intersection type - HomomorphicMappedType = 1 << 1, // Reverse inference for homomorphic mapped type - MappedTypeConstraint = 1 << 2, // Reverse inference for mapped type - ReturnType = 1 << 3, // Inference made from return type of generic function - LiteralKeyof = 1 << 4, // Inference made from a string literal to a keyof T - NoConstraints = 1 << 5, // Don't infer from constraints of instantiable types - AlwaysStrict = 1 << 6, // Always use strict rules for contravariant inferences - - PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates + NakedTypeVariable = 1 << 0, // Naked type variable in union or intersection type + HomomorphicMappedType = 1 << 1, // Reverse inference for homomorphic mapped type + PartialHomomorphicMappedType = 1 << 2, // Partial reverse inference for homomorphic mapped type + MappedTypeConstraint = 1 << 3, // Reverse inference for mapped type + ReturnType = 1 << 4, // Inference made from return type of generic function + LiteralKeyof = 1 << 5, // Inference made from a string literal to a keyof T + NoConstraints = 1 << 6, // Don't infer from constraints of instantiable types + AlwaysStrict = 1 << 7, // Always use strict rules for contravariant inferences + + PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates } /* @internal */