Skip to content

Commit 65f453f

Browse files
committed
Skip unnecessary inference pass
1 parent 73e58ea commit 65f453f

File tree

3 files changed

+76
-71
lines changed

3 files changed

+76
-71
lines changed

src/compiler/checker.ts

Lines changed: 69 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -22947,46 +22947,39 @@ namespace ts {
2294722947
if (!inference.inferredType) {
2294822948
let inferredType: Type | undefined;
2294922949
const signature = context.signature;
22950-
if (!(inference.priority! & InferencePriority.BindingPattern)) {
22951-
// Binding pattern inferences provide a contextual type for other inferences,
22952-
// but cannot stand alone - they are expected to be overwritten by another
22953-
// inference with higher priority before fixing. This prevents highly suspicious
22954-
// patterns like `function f<T>(): T; const { foo } = f()` from inferring T as
22955-
// `{ foo: any }`.
22956-
if (signature) {
22957-
const inferredCovariantType = inference.candidates ? getCovariantInference(inference, signature) : undefined;
22958-
if (inference.contraCandidates) {
22959-
// If we have both co- and contra-variant inferences, we prefer the contra-variant inference
22960-
// unless the co-variant inference is a subtype of some contra-variant inference and not 'never'.
22961-
inferredType = inferredCovariantType && !(inferredCovariantType.flags & TypeFlags.Never) &&
22962-
some(inference.contraCandidates, t => isTypeSubtypeOf(inferredCovariantType, t)) ?
22963-
inferredCovariantType : getContravariantInference(inference);
22964-
}
22965-
else if (inferredCovariantType) {
22966-
inferredType = inferredCovariantType;
22967-
}
22968-
else if (context.flags & InferenceFlags.NoDefault) {
22969-
// We use silentNeverType as the wildcard that signals no inferences.
22970-
inferredType = silentNeverType;
22971-
}
22972-
else {
22973-
// Infer either the default or the empty object type when no inferences were
22974-
// made. It is important to remember that in this case, inference still
22975-
// succeeds, meaning there is no error for not having inference candidates. An
22976-
// inference error only occurs when there are *conflicting* candidates, i.e.
22977-
// candidates with no common supertype.
22978-
const defaultType = getDefaultFromTypeParameter(inference.typeParameter);
22979-
if (defaultType) {
22980-
// Instantiate the default type. Any forward reference to a type
22981-
// parameter should be instantiated to the empty object type.
22982-
inferredType = instantiateType(defaultType, mergeTypeMappers(createBackreferenceMapper(context, index), context.nonFixingMapper));
22983-
}
22984-
}
22950+
if (signature) {
22951+
const inferredCovariantType = inference.candidates ? getCovariantInference(inference, signature) : undefined;
22952+
if (inference.contraCandidates) {
22953+
// If we have both co- and contra-variant inferences, we prefer the contra-variant inference
22954+
// unless the co-variant inference is a subtype of some contra-variant inference and not 'never'.
22955+
inferredType = inferredCovariantType && !(inferredCovariantType.flags & TypeFlags.Never) &&
22956+
some(inference.contraCandidates, t => isTypeSubtypeOf(inferredCovariantType, t)) ?
22957+
inferredCovariantType : getContravariantInference(inference);
22958+
}
22959+
else if (inferredCovariantType) {
22960+
inferredType = inferredCovariantType;
22961+
}
22962+
else if (context.flags & InferenceFlags.NoDefault) {
22963+
// We use silentNeverType as the wildcard that signals no inferences.
22964+
inferredType = silentNeverType;
2298522965
}
2298622966
else {
22987-
inferredType = getTypeFromInference(inference);
22967+
// Infer either the default or the empty object type when no inferences were
22968+
// made. It is important to remember that in this case, inference still
22969+
// succeeds, meaning there is no error for not having inference candidates. An
22970+
// inference error only occurs when there are *conflicting* candidates, i.e.
22971+
// candidates with no common supertype.
22972+
const defaultType = getDefaultFromTypeParameter(inference.typeParameter);
22973+
if (defaultType) {
22974+
// Instantiate the default type. Any forward reference to a type
22975+
// parameter should be instantiated to the empty object type.
22976+
inferredType = instantiateType(defaultType, mergeTypeMappers(createBackreferenceMapper(context, index), context.nonFixingMapper));
22977+
}
2298822978
}
2298922979
}
22980+
else {
22981+
inferredType = getTypeFromInference(inference);
22982+
}
2299022983

2299122984
inference.inferredType = inferredType || getDefaultTypeArgumentType(!!(context.flags & InferenceFlags.AnyDefault));
2299222985

@@ -27062,22 +27055,22 @@ namespace ts {
2706227055
const inferenceContext = getInferenceContext(node);
2706327056
// If no inferences have been made, nothing is gained from instantiating as type parameters
2706427057
// would just be replaced with their defaults similar to the apparent type.
27065-
if (inferenceContext && some(inferenceContext.inferences, hasInferenceCandidates)) {
27058+
if (inferenceContext && contextFlags! & ContextFlags.Signature && (inferenceContext.returnMapper || some(inferenceContext.inferences, hasInferenceCandidates))) {
2706627059
// For contextual signatures we incorporate all inferences made so far, e.g. from return
2706727060
// types as well as arguments to the left in a function call.
27068-
if (contextFlags && contextFlags & ContextFlags.Signature) {
27069-
return instantiateInstantiableTypes(contextualType, inferenceContext.nonFixingMapper);
27070-
}
27061+
return instantiateInstantiableTypes(contextualType, inferenceContext.returnMapper
27062+
? combineTypeMappers(inferenceContext.nonFixingMapper, inferenceContext.returnMapper)
27063+
: inferenceContext.nonFixingMapper);
27064+
}
27065+
if (inferenceContext?.returnMapper) {
2707127066
// For other purposes (e.g. determining whether to produce literal types) we only
2707227067
// incorporate inferences made from the return type in a function call. We remove
2707327068
// the 'boolean' type from the contextual type such that contextually typed boolean
2707427069
// literals actually end up widening to 'boolean' (see #48363).
27075-
if (inferenceContext.returnMapper) {
27076-
const type = instantiateInstantiableTypes(contextualType, inferenceContext.returnMapper);
27077-
return type.flags & TypeFlags.Union && containsType((type as UnionType).types, regularFalseType) && containsType((type as UnionType).types, regularTrueType) ?
27078-
filterType(type, t => t !== regularFalseType && t !== regularTrueType) :
27079-
type;
27080-
}
27070+
const type = instantiateInstantiableTypes(contextualType, inferenceContext.returnMapper);
27071+
return type.flags & TypeFlags.Union && containsType((type as UnionType).types, regularFalseType) && containsType((type as UnionType).types, regularTrueType) ?
27072+
filterType(type, t => t !== regularFalseType && t !== regularTrueType) :
27073+
type;
2708127074
}
2708227075
}
2708327076
return contextualType;
@@ -29918,27 +29911,38 @@ namespace ts {
2991829911
if (contextualType) {
2991929912
const inferenceTargetType = getReturnTypeOfSignature(signature);
2992029913
if (couldContainTypeVariables(inferenceTargetType)) {
29921-
// We clone the inference context to avoid disturbing a resolution in progress for an
29922-
// outer call expression. Effectively we just want a snapshot of whatever has been
29923-
// inferred for any outer call expression so far.
2992429914
const outerContext = getInferenceContext(node);
29925-
const outerMapper = getMapperFromContext(cloneInferenceContext(outerContext, InferenceFlags.NoDefault));
29926-
const instantiatedType = instantiateType(contextualType, outerMapper);
29927-
// If the contextual type is a generic function type with a single call signature, we
29928-
// instantiate the type with its own type parameters and type arguments. This ensures that
29929-
// the type parameters are not erased to type any during type inference such that they can
29930-
// be inferred as actual types from the contextual type. For example:
29931-
// declare function arrayMap<T, U>(f: (x: T) => U): (a: T[]) => U[];
29932-
// const boxElements: <A>(a: A[]) => { value: A }[] = arrayMap(value => ({ value }));
29933-
// Above, the type of the 'value' parameter is inferred to be 'A'.
29934-
const contextualSignature = getSingleCallSignature(instantiatedType);
29935-
const inferenceSourceType = contextualSignature && contextualSignature.typeParameters ?
29936-
getOrCreateTypeFromSignature(getSignatureInstantiationWithoutFillingInTypeArguments(contextualSignature, contextualSignature.typeParameters)) :
29937-
instantiatedType;
29938-
// Inferences made from return types have lower priority than all other inferences.
2993929915
const isFromBindingPattern = !skipBindingPatterns && getContextualType(node, ContextFlags.SkipBindingPatterns) !== contextualType;
29940-
const priority = InferencePriority.ReturnType | (isFromBindingPattern ? InferencePriority.BindingPattern : 0);
29941-
inferTypes(context.inferences, inferenceSourceType, inferenceTargetType, priority);
29916+
// A return type inference from a binding pattern can be used in instantiating the contextual
29917+
// type of an argument later in inference, but cannot stand on its own as the final return type.
29918+
// It is incorporated into `context.returnMapper` which is used in `instantiateContextualType`,
29919+
// but doesn't need to go into `context.inferences`. This allows a an array binding pattern to
29920+
// produce a tuple for `T` in
29921+
// declare function f<T>(cb: () => T): T;
29922+
// const [e1, e2, e3] = f(() => [1, "hi", true]);
29923+
// but does not produce any inference for `T` in
29924+
// declare function f<T>(): T;
29925+
// const [e1, e2, e3] = f();
29926+
if (!isFromBindingPattern) {
29927+
// We clone the inference context to avoid disturbing a resolution in progress for an
29928+
// outer call expression. Effectively we just want a snapshot of whatever has been
29929+
// inferred for any outer call expression so far.
29930+
const outerMapper = getMapperFromContext(cloneInferenceContext(outerContext, InferenceFlags.NoDefault));
29931+
const instantiatedType = instantiateType(contextualType, outerMapper);
29932+
// If the contextual type is a generic function type with a single call signature, we
29933+
// instantiate the type with its own type parameters and type arguments. This ensures that
29934+
// the type parameters are not erased to type any during type inference such that they can
29935+
// be inferred as actual types from the contextual type. For example:
29936+
// declare function arrayMap<T, U>(f: (x: T) => U): (a: T[]) => U[];
29937+
// const boxElements: <A>(a: A[]) => { value: A }[] = arrayMap(value => ({ value }));
29938+
// Above, the type of the 'value' parameter is inferred to be 'A'.
29939+
const contextualSignature = getSingleCallSignature(instantiatedType);
29940+
const inferenceSourceType = contextualSignature && contextualSignature.typeParameters ?
29941+
getOrCreateTypeFromSignature(getSignatureInstantiationWithoutFillingInTypeArguments(contextualSignature, contextualSignature.typeParameters)) :
29942+
instantiatedType;
29943+
// Inferences made from return types have lower priority than all other inferences.
29944+
inferTypes(context.inferences, inferenceSourceType, inferenceTargetType, InferencePriority.ReturnType);
29945+
}
2994229946
// Create a type mapper for instantiating generic contextual types using the inferences made
2994329947
// from the return type. We need a separate inference pass here because (a) instantiation of
2994429948
// the source type uses the outer context's return mapper (which excludes inferences made from

src/compiler/types.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5865,11 +5865,10 @@ namespace ts {
58655865
MappedTypeConstraint = 1 << 5, // Reverse inference for mapped type
58665866
ContravariantConditional = 1 << 6, // Conditional type in contravariant position
58675867
ReturnType = 1 << 7, // Inference made from return type of generic function
5868-
BindingPattern = 1 << 8, // Inference made from binding pattern
5869-
LiteralKeyof = 1 << 9, // Inference made from a string literal to a keyof T
5870-
NoConstraints = 1 << 10, // Don't infer from constraints of instantiable types
5871-
AlwaysStrict = 1 << 11, // Always use strict rules for contravariant inferences
5872-
MaxValue = 1 << 12, // Seed for inference priority tracking
5868+
LiteralKeyof = 1 << 8, // Inference made from a string literal to a keyof T
5869+
NoConstraints = 1 << 9, // Don't infer from constraints of instantiable types
5870+
AlwaysStrict = 1 << 10, // Always use strict rules for contravariant inferences
5871+
MaxValue = 1 << 11, // Seed for inference priority tracking
58735872

58745873
PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates
58755874
Circularity = -1, // Inference circularity (value less than all other priorities)

tests/cases/compiler/bindingPatternCannotBeOnlyInferenceSource.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
// @strictNullChecks: true
2+
13
declare function f<T>(): T;
2-
const {} = f(); // error
4+
const {} = f(); // error (only in strictNullChecks)
35
const { p1 } = f(); // error
46
const [] = f(); // error
57
const [e1, e2] = f(); // error

0 commit comments

Comments
 (0)