Combine keyof T inferences (#22525)

* Combine keyof T inferences

* Extract covariant inference derivation into function

* Test:keyof inference lower priority than return inference

for #22376

* Update 'expected' comment in keyofInferenceLowerPriorityThanReturn

* Update comment in test too, not just baselines

* Fix typo

* Move tests
This commit is contained in:
Wesley Wigham
2018-03-19 16:56:51 -07:00
committed by GitHub
parent b56093f3ac
commit eaabf920eb
12 changed files with 552 additions and 28 deletions

View File

@@ -11704,7 +11704,10 @@ namespace ts {
else if ((isLiteralType(source) || source.flags & TypeFlags.String) && target.flags & TypeFlags.Index) {
const empty = createEmptyObjectTypeFromStringLiteral(source);
contravariant = !contravariant;
const savePriority = priority;
priority |= InferencePriority.LiteralKeyof;
inferFromTypes(empty, (target as IndexType).type);
priority = savePriority;
contravariant = !contravariant;
}
else if (source.flags & TypeFlags.IndexedAccess && target.flags & TypeFlags.IndexedAccess) {
@@ -11947,6 +11950,30 @@ namespace ts {
return candidates;
}
function getContravariantInference(inference: InferenceInfo) {
return inference.priority & InferencePriority.PriorityImpliesCombination ? getIntersectionType(inference.contraCandidates) : getCommonSubtype(inference.contraCandidates);
}
function getCovariantInference(inference: InferenceInfo, context: InferenceContext, signature: Signature) {
// Extract all object literal types and replace them with a single widened and normalized type.
const candidates = widenObjectLiteralCandidates(inference.candidates);
// We widen inferred literal types if
// all inferences were made to top-level occurrences of the type parameter, and
// the type parameter has no constraint or its constraint includes no primitive or literal types, and
// the type parameter was fixed during inference or does not occur at top-level in the return type.
const widenLiteralTypes = inference.topLevel &&
!hasPrimitiveConstraint(inference.typeParameter) &&
(inference.isFixed || !isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), inference.typeParameter));
const baseCandidates = widenLiteralTypes ? sameMap(candidates, getWidenedLiteralType) : candidates;
// If all inferences were made from contravariant positions, infer a common subtype. Otherwise, if
// union types were requested or if all inferences were made from the return type position, infer a
// union type. Otherwise, infer a common supertype.
const unwidenedType = context.flags & InferenceFlags.InferUnionTypes || inference.priority & InferencePriority.PriorityImpliesCombination ?
getUnionType(baseCandidates, UnionReduction.Subtype) :
getCommonSupertype(baseCandidates);
return getWidenedType(unwidenedType);
}
function getInferredType(context: InferenceContext, index: number): Type {
const inference = context.inferences[index];
let inferredType = inference.inferredType;
@@ -11954,32 +11981,16 @@ namespace ts {
const signature = context.signature;
if (signature) {
if (inference.candidates) {
// Extract all object literal types and replace them with a single widened and normalized type.
const candidates = widenObjectLiteralCandidates(inference.candidates);
// We widen inferred literal types if
// all inferences were made to top-level ocurrences of the type parameter, and
// the type parameter has no constraint or its constraint includes no primitive or literal types, and
// the type parameter was fixed during inference or does not occur at top-level in the return type.
const widenLiteralTypes = inference.topLevel &&
!hasPrimitiveConstraint(inference.typeParameter) &&
(inference.isFixed || !isTypeParameterAtTopLevel(getReturnTypeOfSignature(signature), inference.typeParameter));
const baseCandidates = widenLiteralTypes ? sameMap(candidates, getWidenedLiteralType) : candidates;
// If all inferences were made from contravariant positions, infer a common subtype. Otherwise, if
// union types were requested or if all inferences were made from the return type position, infer a
// union type. Otherwise, infer a common supertype.
const unwidenedType = context.flags & InferenceFlags.InferUnionTypes || inference.priority & InferencePriority.PriorityImpliesUnion ?
getUnionType(baseCandidates, UnionReduction.Subtype) :
getCommonSupertype(baseCandidates);
inferredType = getWidenedType(unwidenedType);
inferredType = getCovariantInference(inference, context, signature);
// If we have inferred 'never' but have contravariant candidates. To get a more specific type we
// infer from the contravariant candidates instead.
if (inferredType.flags & TypeFlags.Never && inference.contraCandidates) {
inferredType = getCommonSubtype(inference.contraCandidates);
inferredType = getContravariantInference(inference);
}
}
else if (inference.contraCandidates) {
// We only have contravariant inferences, infer the best common subtype of those
inferredType = getCommonSubtype(inference.contraCandidates);
inferredType = getContravariantInference(inference);
}
else if (context.flags & InferenceFlags.NoDefault) {
// We use silentNeverType as the wildcard that signals no inferences.

View File

@@ -3945,10 +3945,11 @@ namespace ts {
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
NoConstraints = 1 << 4, // Don't infer from constraints of instantiable types
AlwaysStrict = 1 << 5, // Always use strict rules for contravariant inferences
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
PriorityImpliesUnion = ReturnType | MappedTypeConstraint, // These priorities imply that the resulting type should be a union of all candidates
PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates
}
/* @internal */