mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-06-13 22:00:59 -05:00
Skip unnecessary inference pass
This commit is contained in:
@@ -22947,46 +22947,39 @@ namespace ts {
|
||||
if (!inference.inferredType) {
|
||||
let inferredType: Type | undefined;
|
||||
const signature = context.signature;
|
||||
if (!(inference.priority! & InferencePriority.BindingPattern)) {
|
||||
// Binding pattern inferences provide a contextual type for other inferences,
|
||||
// but cannot stand alone - they are expected to be overwritten by another
|
||||
// inference with higher priority before fixing. This prevents highly suspicious
|
||||
// patterns like `function f<T>(): T; const { foo } = f()` from inferring T as
|
||||
// `{ foo: any }`.
|
||||
if (signature) {
|
||||
const inferredCovariantType = inference.candidates ? getCovariantInference(inference, signature) : undefined;
|
||||
if (inference.contraCandidates) {
|
||||
// If we have both co- and contra-variant inferences, we prefer the contra-variant inference
|
||||
// unless the co-variant inference is a subtype of some contra-variant inference and not 'never'.
|
||||
inferredType = inferredCovariantType && !(inferredCovariantType.flags & TypeFlags.Never) &&
|
||||
some(inference.contraCandidates, t => isTypeSubtypeOf(inferredCovariantType, t)) ?
|
||||
inferredCovariantType : getContravariantInference(inference);
|
||||
}
|
||||
else if (inferredCovariantType) {
|
||||
inferredType = inferredCovariantType;
|
||||
}
|
||||
else if (context.flags & InferenceFlags.NoDefault) {
|
||||
// We use silentNeverType as the wildcard that signals no inferences.
|
||||
inferredType = silentNeverType;
|
||||
}
|
||||
else {
|
||||
// Infer either the default or the empty object type when no inferences were
|
||||
// made. It is important to remember that in this case, inference still
|
||||
// succeeds, meaning there is no error for not having inference candidates. An
|
||||
// inference error only occurs when there are *conflicting* candidates, i.e.
|
||||
// candidates with no common supertype.
|
||||
const defaultType = getDefaultFromTypeParameter(inference.typeParameter);
|
||||
if (defaultType) {
|
||||
// Instantiate the default type. Any forward reference to a type
|
||||
// parameter should be instantiated to the empty object type.
|
||||
inferredType = instantiateType(defaultType, mergeTypeMappers(createBackreferenceMapper(context, index), context.nonFixingMapper));
|
||||
}
|
||||
}
|
||||
if (signature) {
|
||||
const inferredCovariantType = inference.candidates ? getCovariantInference(inference, signature) : undefined;
|
||||
if (inference.contraCandidates) {
|
||||
// If we have both co- and contra-variant inferences, we prefer the contra-variant inference
|
||||
// unless the co-variant inference is a subtype of some contra-variant inference and not 'never'.
|
||||
inferredType = inferredCovariantType && !(inferredCovariantType.flags & TypeFlags.Never) &&
|
||||
some(inference.contraCandidates, t => isTypeSubtypeOf(inferredCovariantType, t)) ?
|
||||
inferredCovariantType : getContravariantInference(inference);
|
||||
}
|
||||
else if (inferredCovariantType) {
|
||||
inferredType = inferredCovariantType;
|
||||
}
|
||||
else if (context.flags & InferenceFlags.NoDefault) {
|
||||
// We use silentNeverType as the wildcard that signals no inferences.
|
||||
inferredType = silentNeverType;
|
||||
}
|
||||
else {
|
||||
inferredType = getTypeFromInference(inference);
|
||||
// Infer either the default or the empty object type when no inferences were
|
||||
// made. It is important to remember that in this case, inference still
|
||||
// succeeds, meaning there is no error for not having inference candidates. An
|
||||
// inference error only occurs when there are *conflicting* candidates, i.e.
|
||||
// candidates with no common supertype.
|
||||
const defaultType = getDefaultFromTypeParameter(inference.typeParameter);
|
||||
if (defaultType) {
|
||||
// Instantiate the default type. Any forward reference to a type
|
||||
// parameter should be instantiated to the empty object type.
|
||||
inferredType = instantiateType(defaultType, mergeTypeMappers(createBackreferenceMapper(context, index), context.nonFixingMapper));
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
inferredType = getTypeFromInference(inference);
|
||||
}
|
||||
|
||||
inference.inferredType = inferredType || getDefaultTypeArgumentType(!!(context.flags & InferenceFlags.AnyDefault));
|
||||
|
||||
@@ -27062,22 +27055,22 @@ namespace ts {
|
||||
const inferenceContext = getInferenceContext(node);
|
||||
// If no inferences have been made, nothing is gained from instantiating as type parameters
|
||||
// would just be replaced with their defaults similar to the apparent type.
|
||||
if (inferenceContext && some(inferenceContext.inferences, hasInferenceCandidates)) {
|
||||
if (inferenceContext && contextFlags! & ContextFlags.Signature && (inferenceContext.returnMapper || some(inferenceContext.inferences, hasInferenceCandidates))) {
|
||||
// For contextual signatures we incorporate all inferences made so far, e.g. from return
|
||||
// types as well as arguments to the left in a function call.
|
||||
if (contextFlags && contextFlags & ContextFlags.Signature) {
|
||||
return instantiateInstantiableTypes(contextualType, inferenceContext.nonFixingMapper);
|
||||
}
|
||||
return instantiateInstantiableTypes(contextualType, inferenceContext.returnMapper
|
||||
? combineTypeMappers(inferenceContext.nonFixingMapper, inferenceContext.returnMapper)
|
||||
: inferenceContext.nonFixingMapper);
|
||||
}
|
||||
if (inferenceContext?.returnMapper) {
|
||||
// For other purposes (e.g. determining whether to produce literal types) we only
|
||||
// incorporate inferences made from the return type in a function call. We remove
|
||||
// the 'boolean' type from the contextual type such that contextually typed boolean
|
||||
// literals actually end up widening to 'boolean' (see #48363).
|
||||
if (inferenceContext.returnMapper) {
|
||||
const type = instantiateInstantiableTypes(contextualType, inferenceContext.returnMapper);
|
||||
return type.flags & TypeFlags.Union && containsType((type as UnionType).types, regularFalseType) && containsType((type as UnionType).types, regularTrueType) ?
|
||||
filterType(type, t => t !== regularFalseType && t !== regularTrueType) :
|
||||
type;
|
||||
}
|
||||
const type = instantiateInstantiableTypes(contextualType, inferenceContext.returnMapper);
|
||||
return type.flags & TypeFlags.Union && containsType((type as UnionType).types, regularFalseType) && containsType((type as UnionType).types, regularTrueType) ?
|
||||
filterType(type, t => t !== regularFalseType && t !== regularTrueType) :
|
||||
type;
|
||||
}
|
||||
}
|
||||
return contextualType;
|
||||
@@ -29918,27 +29911,38 @@ namespace ts {
|
||||
if (contextualType) {
|
||||
const inferenceTargetType = getReturnTypeOfSignature(signature);
|
||||
if (couldContainTypeVariables(inferenceTargetType)) {
|
||||
// We clone the inference context to avoid disturbing a resolution in progress for an
|
||||
// outer call expression. Effectively we just want a snapshot of whatever has been
|
||||
// inferred for any outer call expression so far.
|
||||
const outerContext = getInferenceContext(node);
|
||||
const outerMapper = getMapperFromContext(cloneInferenceContext(outerContext, InferenceFlags.NoDefault));
|
||||
const instantiatedType = instantiateType(contextualType, outerMapper);
|
||||
// If the contextual type is a generic function type with a single call signature, we
|
||||
// instantiate the type with its own type parameters and type arguments. This ensures that
|
||||
// the type parameters are not erased to type any during type inference such that they can
|
||||
// be inferred as actual types from the contextual type. For example:
|
||||
// declare function arrayMap<T, U>(f: (x: T) => U): (a: T[]) => U[];
|
||||
// const boxElements: <A>(a: A[]) => { value: A }[] = arrayMap(value => ({ value }));
|
||||
// Above, the type of the 'value' parameter is inferred to be 'A'.
|
||||
const contextualSignature = getSingleCallSignature(instantiatedType);
|
||||
const inferenceSourceType = contextualSignature && contextualSignature.typeParameters ?
|
||||
getOrCreateTypeFromSignature(getSignatureInstantiationWithoutFillingInTypeArguments(contextualSignature, contextualSignature.typeParameters)) :
|
||||
instantiatedType;
|
||||
// Inferences made from return types have lower priority than all other inferences.
|
||||
const isFromBindingPattern = !skipBindingPatterns && getContextualType(node, ContextFlags.SkipBindingPatterns) !== contextualType;
|
||||
const priority = InferencePriority.ReturnType | (isFromBindingPattern ? InferencePriority.BindingPattern : 0);
|
||||
inferTypes(context.inferences, inferenceSourceType, inferenceTargetType, priority);
|
||||
// A return type inference from a binding pattern can be used in instantiating the contextual
|
||||
// type of an argument later in inference, but cannot stand on its own as the final return type.
|
||||
// It is incorporated into `context.returnMapper` which is used in `instantiateContextualType`,
|
||||
// but doesn't need to go into `context.inferences`. This allows a an array binding pattern to
|
||||
// produce a tuple for `T` in
|
||||
// declare function f<T>(cb: () => T): T;
|
||||
// const [e1, e2, e3] = f(() => [1, "hi", true]);
|
||||
// but does not produce any inference for `T` in
|
||||
// declare function f<T>(): T;
|
||||
// const [e1, e2, e3] = f();
|
||||
if (!isFromBindingPattern) {
|
||||
// We clone the inference context to avoid disturbing a resolution in progress for an
|
||||
// outer call expression. Effectively we just want a snapshot of whatever has been
|
||||
// inferred for any outer call expression so far.
|
||||
const outerMapper = getMapperFromContext(cloneInferenceContext(outerContext, InferenceFlags.NoDefault));
|
||||
const instantiatedType = instantiateType(contextualType, outerMapper);
|
||||
// If the contextual type is a generic function type with a single call signature, we
|
||||
// instantiate the type with its own type parameters and type arguments. This ensures that
|
||||
// the type parameters are not erased to type any during type inference such that they can
|
||||
// be inferred as actual types from the contextual type. For example:
|
||||
// declare function arrayMap<T, U>(f: (x: T) => U): (a: T[]) => U[];
|
||||
// const boxElements: <A>(a: A[]) => { value: A }[] = arrayMap(value => ({ value }));
|
||||
// Above, the type of the 'value' parameter is inferred to be 'A'.
|
||||
const contextualSignature = getSingleCallSignature(instantiatedType);
|
||||
const inferenceSourceType = contextualSignature && contextualSignature.typeParameters ?
|
||||
getOrCreateTypeFromSignature(getSignatureInstantiationWithoutFillingInTypeArguments(contextualSignature, contextualSignature.typeParameters)) :
|
||||
instantiatedType;
|
||||
// Inferences made from return types have lower priority than all other inferences.
|
||||
inferTypes(context.inferences, inferenceSourceType, inferenceTargetType, InferencePriority.ReturnType);
|
||||
}
|
||||
// Create a type mapper for instantiating generic contextual types using the inferences made
|
||||
// from the return type. We need a separate inference pass here because (a) instantiation of
|
||||
// the source type uses the outer context's return mapper (which excludes inferences made from
|
||||
|
||||
@@ -5865,11 +5865,10 @@ namespace ts {
|
||||
MappedTypeConstraint = 1 << 5, // Reverse inference for mapped type
|
||||
ContravariantConditional = 1 << 6, // Conditional type in contravariant position
|
||||
ReturnType = 1 << 7, // Inference made from return type of generic function
|
||||
BindingPattern = 1 << 8, // Inference made from binding pattern
|
||||
LiteralKeyof = 1 << 9, // Inference made from a string literal to a keyof T
|
||||
NoConstraints = 1 << 10, // Don't infer from constraints of instantiable types
|
||||
AlwaysStrict = 1 << 11, // Always use strict rules for contravariant inferences
|
||||
MaxValue = 1 << 12, // Seed for inference priority tracking
|
||||
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
|
||||
|
||||
PriorityImpliesCombination = ReturnType | 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)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
// @strictNullChecks: true
|
||||
|
||||
declare function f<T>(): T;
|
||||
const {} = f(); // error
|
||||
const {} = f(); // error (only in strictNullChecks)
|
||||
const { p1 } = f(); // error
|
||||
const [] = f(); // error
|
||||
const [e1, e2] = f(); // error
|
||||
|
||||
Reference in New Issue
Block a user