@@ -21729,6 +21729,9 @@ namespace ts {
21729
21729
const inference = inferences[i];
21730
21730
if (t === inference.typeParameter) {
21731
21731
if (fix && !inference.isFixed) {
21732
+ // Before we commit to a particular inference (and thus lock out any further inferences),
21733
+ // we infer from any intra-expression inference sites we have collected.
21734
+ inferFromIntraExpressionSites(context);
21732
21735
clearCachedInferences(inferences);
21733
21736
inference.isFixed = true;
21734
21737
}
@@ -21746,6 +21749,37 @@ namespace ts {
21746
21749
}
21747
21750
}
21748
21751
21752
+ function addIntraExpressionInferenceSite(context: InferenceContext, node: Expression | MethodDeclaration, type: Type) {
21753
+ (context.intraExpressionInferenceSites ??= []).push({ node, type });
21754
+ }
21755
+
21756
+ // We collect intra-expression inference sites within object and array literals to handle cases where
21757
+ // inferred types flow between context sensitive element expressions. For example:
21758
+ //
21759
+ // declare function foo<T>(arg: [(n: number) => T, (x: T) => void]): void;
21760
+ // foo([_a => 0, n => n.toFixed()]);
21761
+ //
21762
+ // Above, both arrow functions in the tuple argument are context sensitive, thus both are omitted from the
21763
+ // pass that collects inferences from the non-context sensitive parts of the arguments. In the subsequent
21764
+ // pass where nothing is omitted, we need to commit to an inference for T in order to contextually type the
21765
+ // parameter in the second arrow function, but we want to first infer from the return type of the first
21766
+ // arrow function. This happens automatically when the arrow functions are discrete arguments (because we
21767
+ // infer from each argument before processing the next), but when the arrow functions are elements of an
21768
+ // object or array literal, we need to perform intra-expression inferences early.
21769
+ function inferFromIntraExpressionSites(context: InferenceContext) {
21770
+ if (context.intraExpressionInferenceSites) {
21771
+ for (const { node, type } of context.intraExpressionInferenceSites) {
21772
+ const contextualType = node.kind === SyntaxKind.MethodDeclaration ?
21773
+ getContextualTypeForObjectLiteralMethod(node as MethodDeclaration, ContextFlags.NoConstraints) :
21774
+ getContextualType(node, ContextFlags.NoConstraints);
21775
+ if (contextualType) {
21776
+ inferTypes(context.inferences, type, contextualType);
21777
+ }
21778
+ }
21779
+ context.intraExpressionInferenceSites = undefined;
21780
+ }
21781
+ }
21782
+
21749
21783
function createInferenceInfo(typeParameter: TypeParameter): InferenceInfo {
21750
21784
return {
21751
21785
typeParameter,
@@ -27408,6 +27442,11 @@ namespace ts {
27408
27442
const type = checkExpressionForMutableLocation(e, checkMode, elementContextualType, forceTuple);
27409
27443
elementTypes.push(addOptionality(type, /*isProperty*/ true, hasOmittedExpression));
27410
27444
elementFlags.push(hasOmittedExpression ? ElementFlags.Optional : ElementFlags.Required);
27445
+ if (contextualType && someType(contextualType, isTupleLikeType) && checkMode && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) && isContextSensitive(e)) {
27446
+ const inferenceContext = getInferenceContext(node);
27447
+ Debug.assert(inferenceContext); // In CheckMode.Inferential we should always have an inference context
27448
+ addIntraExpressionInferenceSite(inferenceContext, e, type);
27449
+ }
27411
27450
}
27412
27451
}
27413
27452
if (inDestructuringPattern) {
@@ -27625,6 +27664,14 @@ namespace ts {
27625
27664
prop.target = member;
27626
27665
member = prop;
27627
27666
allPropertiesTable?.set(prop.escapedName, prop);
27667
+
27668
+ if (contextualType && checkMode && checkMode & CheckMode.Inferential && !(checkMode & CheckMode.SkipContextSensitive) &&
27669
+ (memberDecl.kind === SyntaxKind.PropertyAssignment || memberDecl.kind === SyntaxKind.MethodDeclaration) && isContextSensitive(memberDecl)) {
27670
+ const inferenceContext = getInferenceContext(node);
27671
+ Debug.assert(inferenceContext); // In CheckMode.Inferential we should always have an inference context
27672
+ const inferenceNode = memberDecl.kind === SyntaxKind.PropertyAssignment ? memberDecl.initializer : memberDecl;
27673
+ addIntraExpressionInferenceSite(inferenceContext, inferenceNode, type);
27674
+ }
27628
27675
}
27629
27676
else if (memberDecl.kind === SyntaxKind.SpreadAssignment) {
27630
27677
if (languageVersion < ScriptTarget.ES2015) {
@@ -29727,34 +29774,36 @@ namespace ts {
29727
29774
if (node.kind !== SyntaxKind.Decorator) {
29728
29775
const contextualType = getContextualType(node, every(signature.typeParameters, p => !!getDefaultFromTypeParameter(p)) ? ContextFlags.SkipBindingPatterns : ContextFlags.None);
29729
29776
if (contextualType) {
29730
- // We clone the inference context to avoid disturbing a resolution in progress for an
29731
- // outer call expression. Effectively we just want a snapshot of whatever has been
29732
- // inferred for any outer call expression so far.
29733
- const outerContext = getInferenceContext(node);
29734
- const outerMapper = getMapperFromContext(cloneInferenceContext(outerContext, InferenceFlags.NoDefault));
29735
- const instantiatedType = instantiateType(contextualType, outerMapper);
29736
- // If the contextual type is a generic function type with a single call signature, we
29737
- // instantiate the type with its own type parameters and type arguments. This ensures that
29738
- // the type parameters are not erased to type any during type inference such that they can
29739
- // be inferred as actual types from the contextual type. For example:
29740
- // declare function arrayMap<T, U>(f: (x: T) => U): (a: T[]) => U[];
29741
- // const boxElements: <A>(a: A[]) => { value: A }[] = arrayMap(value => ({ value }));
29742
- // Above, the type of the 'value' parameter is inferred to be 'A'.
29743
- const contextualSignature = getSingleCallSignature(instantiatedType);
29744
- const inferenceSourceType = contextualSignature && contextualSignature.typeParameters ?
29745
- getOrCreateTypeFromSignature(getSignatureInstantiationWithoutFillingInTypeArguments(contextualSignature, contextualSignature.typeParameters)) :
29746
- instantiatedType;
29747
29777
const inferenceTargetType = getReturnTypeOfSignature(signature);
29748
- // Inferences made from return types have lower priority than all other inferences.
29749
- inferTypes(context.inferences, inferenceSourceType, inferenceTargetType, InferencePriority.ReturnType);
29750
- // Create a type mapper for instantiating generic contextual types using the inferences made
29751
- // from the return type. We need a separate inference pass here because (a) instantiation of
29752
- // the source type uses the outer context's return mapper (which excludes inferences made from
29753
- // outer arguments), and (b) we don't want any further inferences going into this context.
29754
- const returnContext = createInferenceContext(signature.typeParameters!, signature, context.flags);
29755
- const returnSourceType = instantiateType(contextualType, outerContext && outerContext.returnMapper);
29756
- inferTypes(returnContext.inferences, returnSourceType, inferenceTargetType);
29757
- context.returnMapper = some(returnContext.inferences, hasInferenceCandidates) ? getMapperFromContext(cloneInferredPartOfContext(returnContext)) : undefined;
29778
+ if (couldContainTypeVariables(inferenceTargetType)) {
29779
+ // We clone the inference context to avoid disturbing a resolution in progress for an
29780
+ // outer call expression. Effectively we just want a snapshot of whatever has been
29781
+ // inferred for any outer call expression so far.
29782
+ const outerContext = getInferenceContext(node);
29783
+ const outerMapper = getMapperFromContext(cloneInferenceContext(outerContext, InferenceFlags.NoDefault));
29784
+ const instantiatedType = instantiateType(contextualType, outerMapper);
29785
+ // If the contextual type is a generic function type with a single call signature, we
29786
+ // instantiate the type with its own type parameters and type arguments. This ensures that
29787
+ // the type parameters are not erased to type any during type inference such that they can
29788
+ // be inferred as actual types from the contextual type. For example:
29789
+ // declare function arrayMap<T, U>(f: (x: T) => U): (a: T[]) => U[];
29790
+ // const boxElements: <A>(a: A[]) => { value: A }[] = arrayMap(value => ({ value }));
29791
+ // Above, the type of the 'value' parameter is inferred to be 'A'.
29792
+ const contextualSignature = getSingleCallSignature(instantiatedType);
29793
+ const inferenceSourceType = contextualSignature && contextualSignature.typeParameters ?
29794
+ getOrCreateTypeFromSignature(getSignatureInstantiationWithoutFillingInTypeArguments(contextualSignature, contextualSignature.typeParameters)) :
29795
+ instantiatedType;
29796
+ // Inferences made from return types have lower priority than all other inferences.
29797
+ inferTypes(context.inferences, inferenceSourceType, inferenceTargetType, InferencePriority.ReturnType);
29798
+ // Create a type mapper for instantiating generic contextual types using the inferences made
29799
+ // from the return type. We need a separate inference pass here because (a) instantiation of
29800
+ // the source type uses the outer context's return mapper (which excludes inferences made from
29801
+ // outer arguments), and (b) we don't want any further inferences going into this context.
29802
+ const returnContext = createInferenceContext(signature.typeParameters!, signature, context.flags);
29803
+ const returnSourceType = instantiateType(contextualType, outerContext && outerContext.returnMapper);
29804
+ inferTypes(returnContext.inferences, returnSourceType, inferenceTargetType);
29805
+ context.returnMapper = some(returnContext.inferences, hasInferenceCandidates) ? getMapperFromContext(cloneInferredPartOfContext(returnContext)) : undefined;
29806
+ }
29758
29807
}
29759
29808
}
29760
29809
@@ -29768,7 +29817,7 @@ namespace ts {
29768
29817
}
29769
29818
29770
29819
const thisType = getThisTypeOfSignature(signature);
29771
- if (thisType) {
29820
+ if (thisType && couldContainTypeVariables(thisType) ) {
29772
29821
const thisArgumentNode = getThisArgumentOfCall(node);
29773
29822
inferTypes(context.inferences, getThisArgumentType(thisArgumentNode), thisType);
29774
29823
}
@@ -29777,12 +29826,14 @@ namespace ts {
29777
29826
const arg = args[i];
29778
29827
if (arg.kind !== SyntaxKind.OmittedExpression && !(checkMode & CheckMode.IsForStringLiteralArgumentCompletions && hasSkipDirectInferenceFlag(arg))) {
29779
29828
const paramType = getTypeAtPosition(signature, i);
29780
- const argType = checkExpressionWithContextualType(arg, paramType, context, checkMode);
29781
- inferTypes(context.inferences, argType, paramType);
29829
+ if (couldContainTypeVariables(paramType)) {
29830
+ const argType = checkExpressionWithContextualType(arg, paramType, context, checkMode);
29831
+ inferTypes(context.inferences, argType, paramType);
29832
+ }
29782
29833
}
29783
29834
}
29784
29835
29785
- if (restType) {
29836
+ if (restType && couldContainTypeVariables(restType) ) {
29786
29837
const spreadType = getSpreadArgumentType(args, argCount, args.length, restType, context, checkMode);
29787
29838
inferTypes(context.inferences, spreadType, restType);
29788
29839
}
@@ -34141,6 +34192,11 @@ namespace ts {
34141
34192
context.contextualType = contextualType;
34142
34193
context.inferenceContext = inferenceContext;
34143
34194
const type = checkExpression(node, checkMode | CheckMode.Contextual | (inferenceContext ? CheckMode.Inferential : 0));
34195
+ // In CheckMode.Inferential we collect intra-expression inference sites to process before fixing any type
34196
+ // parameters. This information is no longer needed after the call to checkExpression.
34197
+ if (inferenceContext && inferenceContext.intraExpressionInferenceSites) {
34198
+ inferenceContext.intraExpressionInferenceSites = undefined;
34199
+ }
34144
34200
// We strip literal freshness when an appropriate contextual type is present such that contextually typed
34145
34201
// literals always preserve their literal types (otherwise they might widen during type inference). An alternative
34146
34202
// here would be to not mark contextually typed literals as fresh in the first place.
0 commit comments