@@ -21749,6 +21749,9 @@ namespace ts {
21749
21749
const inference = inferences[i];
21750
21750
if (t === inference.typeParameter) {
21751
21751
if (fix && !inference.isFixed) {
21752
+ // Before we commit to a particular inference (and thus lock out any further inferences),
21753
+ // we infer from any intra-expression inference sites we have collected.
21754
+ inferFromIntraExpressionSites(context);
21752
21755
clearCachedInferences(inferences);
21753
21756
inference.isFixed = true;
21754
21757
}
@@ -21766,6 +21769,37 @@ namespace ts {
21766
21769
}
21767
21770
}
21768
21771
21772
+ function addIntraExpressionInferenceSite(context: InferenceContext, node: Expression | MethodDeclaration, type: Type) {
21773
+ (context.intraExpressionInferenceSites ??= []).push({ node, type });
21774
+ }
21775
+
21776
+ // We collect intra-expression inference sites within object and array literals to handle cases where
21777
+ // inferred types flow between context sensitive element expressions. For example:
21778
+ //
21779
+ // declare function foo<T>(arg: [(n: number) => T, (x: T) => void]): void;
21780
+ // foo([_a => 0, n => n.toFixed()]);
21781
+ //
21782
+ // Above, both arrow functions in the tuple argument are context sensitive, thus both are omitted from the
21783
+ // pass that collects inferences from the non-context sensitive parts of the arguments. In the subsequent
21784
+ // pass where nothing is omitted, we need to commit to an inference for T in order to contextually type the
21785
+ // parameter in the second arrow function, but we want to first infer from the return type of the first
21786
+ // arrow function. This happens automatically when the arrow functions are discrete arguments (because we
21787
+ // infer from each argument before processing the next), but when the arrow functions are elements of an
21788
+ // object or array literal, we need to perform intra-expression inferences early.
21789
+ function inferFromIntraExpressionSites(context: InferenceContext) {
21790
+ if (context.intraExpressionInferenceSites) {
21791
+ for (const { node, type } of context.intraExpressionInferenceSites) {
21792
+ const contextualType = node.kind === SyntaxKind.MethodDeclaration ?
21793
+ getContextualTypeForObjectLiteralMethod(node as MethodDeclaration, ContextFlags.NoConstraints) :
21794
+ getContextualType(node, ContextFlags.NoConstraints);
21795
+ if (contextualType) {
21796
+ inferTypes(context.inferences, type, contextualType);
21797
+ }
21798
+ }
21799
+ context.intraExpressionInferenceSites = undefined;
21800
+ }
21801
+ }
21802
+
21769
21803
function createInferenceInfo(typeParameter: TypeParameter): InferenceInfo {
21770
21804
return {
21771
21805
typeParameter,
@@ -27429,6 +27463,11 @@ namespace ts {
27429
27463
const type = checkExpressionForMutableLocation(e, checkMode, elementContextualType, forceTuple);
27430
27464
elementTypes.push(addOptionality(type, /*isProperty*/ true, hasOmittedExpression));
27431
27465
elementFlags.push(hasOmittedExpression ? ElementFlags.Optional : ElementFlags.Required);
27466
+ if (contextualType && someType(contextualType, isTupleLikeType) && checkMode && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) && isContextSensitive(e)) {
27467
+ const inferenceContext = getInferenceContext(node);
27468
+ Debug.assert(inferenceContext); // In CheckMode.Inferential we should always have an inference context
27469
+ addIntraExpressionInferenceSite(inferenceContext, e, type);
27470
+ }
27432
27471
}
27433
27472
}
27434
27473
if (inDestructuringPattern) {
@@ -27646,6 +27685,14 @@ namespace ts {
27646
27685
prop.target = member;
27647
27686
member = prop;
27648
27687
allPropertiesTable?.set(prop.escapedName, prop);
27688
+
27689
+ if (contextualType && checkMode && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) &&
27690
+ (memberDecl.kind === SyntaxKind.PropertyAssignment || memberDecl.kind === SyntaxKind.MethodDeclaration) && isContextSensitive(memberDecl)) {
27691
+ const inferenceContext = getInferenceContext(node);
27692
+ Debug.assert(inferenceContext); // In CheckMode.Inferential we should always have an inference context
27693
+ const inferenceNode = memberDecl.kind === SyntaxKind.PropertyAssignment ? memberDecl.initializer : memberDecl;
27694
+ addIntraExpressionInferenceSite(inferenceContext, inferenceNode, type);
27695
+ }
27649
27696
}
27650
27697
else if (memberDecl.kind === SyntaxKind.SpreadAssignment) {
27651
27698
if (languageVersion < ScriptTarget.ES2015) {
@@ -29748,34 +29795,36 @@ namespace ts {
29748
29795
if (node.kind !== SyntaxKind.Decorator) {
29749
29796
const contextualType = getContextualType(node, every(signature.typeParameters, p => !!getDefaultFromTypeParameter(p)) ? ContextFlags.SkipBindingPatterns : ContextFlags.None);
29750
29797
if (contextualType) {
29751
- // We clone the inference context to avoid disturbing a resolution in progress for an
29752
- // outer call expression. Effectively we just want a snapshot of whatever has been
29753
- // inferred for any outer call expression so far.
29754
- const outerContext = getInferenceContext(node);
29755
- const outerMapper = getMapperFromContext(cloneInferenceContext(outerContext, InferenceFlags.NoDefault));
29756
- const instantiatedType = instantiateType(contextualType, outerMapper);
29757
- // If the contextual type is a generic function type with a single call signature, we
29758
- // instantiate the type with its own type parameters and type arguments. This ensures that
29759
- // the type parameters are not erased to type any during type inference such that they can
29760
- // be inferred as actual types from the contextual type. For example:
29761
- // declare function arrayMap<T, U>(f: (x: T) => U): (a: T[]) => U[];
29762
- // const boxElements: <A>(a: A[]) => { value: A }[] = arrayMap(value => ({ value }));
29763
- // Above, the type of the 'value' parameter is inferred to be 'A'.
29764
- const contextualSignature = getSingleCallSignature(instantiatedType);
29765
- const inferenceSourceType = contextualSignature && contextualSignature.typeParameters ?
29766
- getOrCreateTypeFromSignature(getSignatureInstantiationWithoutFillingInTypeArguments(contextualSignature, contextualSignature.typeParameters)) :
29767
- instantiatedType;
29768
29798
const inferenceTargetType = getReturnTypeOfSignature(signature);
29769
- // Inferences made from return types have lower priority than all other inferences.
29770
- inferTypes(context.inferences, inferenceSourceType, inferenceTargetType, InferencePriority.ReturnType);
29771
- // Create a type mapper for instantiating generic contextual types using the inferences made
29772
- // from the return type. We need a separate inference pass here because (a) instantiation of
29773
- // the source type uses the outer context's return mapper (which excludes inferences made from
29774
- // outer arguments), and (b) we don't want any further inferences going into this context.
29775
- const returnContext = createInferenceContext(signature.typeParameters!, signature, context.flags);
29776
- const returnSourceType = instantiateType(contextualType, outerContext && outerContext.returnMapper);
29777
- inferTypes(returnContext.inferences, returnSourceType, inferenceTargetType);
29778
- context.returnMapper = some(returnContext.inferences, hasInferenceCandidates) ? getMapperFromContext(cloneInferredPartOfContext(returnContext)) : undefined;
29799
+ if (couldContainTypeVariables(inferenceTargetType)) {
29800
+ // We clone the inference context to avoid disturbing a resolution in progress for an
29801
+ // outer call expression. Effectively we just want a snapshot of whatever has been
29802
+ // inferred for any outer call expression so far.
29803
+ const outerContext = getInferenceContext(node);
29804
+ const outerMapper = getMapperFromContext(cloneInferenceContext(outerContext, InferenceFlags.NoDefault));
29805
+ const instantiatedType = instantiateType(contextualType, outerMapper);
29806
+ // If the contextual type is a generic function type with a single call signature, we
29807
+ // instantiate the type with its own type parameters and type arguments. This ensures that
29808
+ // the type parameters are not erased to type any during type inference such that they can
29809
+ // be inferred as actual types from the contextual type. For example:
29810
+ // declare function arrayMap<T, U>(f: (x: T) => U): (a: T[]) => U[];
29811
+ // const boxElements: <A>(a: A[]) => { value: A }[] = arrayMap(value => ({ value }));
29812
+ // Above, the type of the 'value' parameter is inferred to be 'A'.
29813
+ const contextualSignature = getSingleCallSignature(instantiatedType);
29814
+ const inferenceSourceType = contextualSignature && contextualSignature.typeParameters ?
29815
+ getOrCreateTypeFromSignature(getSignatureInstantiationWithoutFillingInTypeArguments(contextualSignature, contextualSignature.typeParameters)) :
29816
+ instantiatedType;
29817
+ // Inferences made from return types have lower priority than all other inferences.
29818
+ inferTypes(context.inferences, inferenceSourceType, inferenceTargetType, InferencePriority.ReturnType);
29819
+ // Create a type mapper for instantiating generic contextual types using the inferences made
29820
+ // from the return type. We need a separate inference pass here because (a) instantiation of
29821
+ // the source type uses the outer context's return mapper (which excludes inferences made from
29822
+ // outer arguments), and (b) we don't want any further inferences going into this context.
29823
+ const returnContext = createInferenceContext(signature.typeParameters!, signature, context.flags);
29824
+ const returnSourceType = instantiateType(contextualType, outerContext && outerContext.returnMapper);
29825
+ inferTypes(returnContext.inferences, returnSourceType, inferenceTargetType);
29826
+ context.returnMapper = some(returnContext.inferences, hasInferenceCandidates) ? getMapperFromContext(cloneInferredPartOfContext(returnContext)) : undefined;
29827
+ }
29779
29828
}
29780
29829
}
29781
29830
@@ -29789,7 +29838,7 @@ namespace ts {
29789
29838
}
29790
29839
29791
29840
const thisType = getThisTypeOfSignature(signature);
29792
- if (thisType) {
29841
+ if (thisType && couldContainTypeVariables(thisType) ) {
29793
29842
const thisArgumentNode = getThisArgumentOfCall(node);
29794
29843
inferTypes(context.inferences, getThisArgumentType(thisArgumentNode), thisType);
29795
29844
}
@@ -29798,12 +29847,14 @@ namespace ts {
29798
29847
const arg = args[i];
29799
29848
if (arg.kind !== SyntaxKind.OmittedExpression && !(checkMode & CheckMode.IsForStringLiteralArgumentCompletions && hasSkipDirectInferenceFlag(arg))) {
29800
29849
const paramType = getTypeAtPosition(signature, i);
29801
- const argType = checkExpressionWithContextualType(arg, paramType, context, checkMode);
29802
- inferTypes(context.inferences, argType, paramType);
29850
+ if (couldContainTypeVariables(paramType)) {
29851
+ const argType = checkExpressionWithContextualType(arg, paramType, context, checkMode);
29852
+ inferTypes(context.inferences, argType, paramType);
29853
+ }
29803
29854
}
29804
29855
}
29805
29856
29806
- if (restType) {
29857
+ if (restType && couldContainTypeVariables(restType) ) {
29807
29858
const spreadType = getSpreadArgumentType(args, argCount, args.length, restType, context, checkMode);
29808
29859
inferTypes(context.inferences, spreadType, restType);
29809
29860
}
@@ -34162,6 +34213,11 @@ namespace ts {
34162
34213
context.contextualType = contextualType;
34163
34214
context.inferenceContext = inferenceContext;
34164
34215
const type = checkExpression(node, checkMode | CheckMode.Contextual | (inferenceContext ? CheckMode.Inferential : 0));
34216
+ // In CheckMode.Inferential we collect intra-expression inference sites to process before fixing any type
34217
+ // parameters. This information is no longer needed after the call to checkExpression.
34218
+ if (inferenceContext && inferenceContext.intraExpressionInferenceSites) {
34219
+ inferenceContext.intraExpressionInferenceSites = undefined;
34220
+ }
34165
34221
// We strip literal freshness when an appropriate contextual type is present such that contextually typed
34166
34222
// literals always preserve their literal types (otherwise they might widen during type inference). An alternative
34167
34223
// here would be to not mark contextually typed literals as fresh in the first place.
0 commit comments