@@ -4249,15 +4249,18 @@ namespace ts {
42494249 }
42504250
42514251 function createInferenceMapper(context: InferenceContext): TypeMapper {
4252- return t => {
4252+ let mapper: TypeMapper = t => {
42534253 for (let i = 0; i < context.typeParameters.length; i++) {
42544254 if (t === context.typeParameters[i]) {
42554255 context.inferences[i].isFixed = true;
42564256 return getInferredType(context, i);
42574257 }
42584258 }
4259- return t;
4259+ return t;
42604260 };
4261+
4262+ mapper.context = context;
4263+ return mapper;
42614264 }
42624265
42634266 function identityMapper(type: Type): Type {
@@ -5468,7 +5471,9 @@ namespace ts {
54685471 function createInferenceContext(typeParameters: TypeParameter[], inferUnionTypes: boolean): InferenceContext {
54695472 let inferences: TypeInferences[] = [];
54705473 for (let unused of typeParameters) {
5471- inferences.push({ primary: undefined, secondary: undefined, isFixed: false });
5474+ inferences.push({
5475+ primary: undefined, secondary: undefined, isFixed: false
5476+ });
54725477 }
54735478 return {
54745479 typeParameters,
@@ -6769,10 +6774,23 @@ namespace ts {
67696774 return result;
67706775 }
67716776
6772- // Presence of a contextual type mapper indicates inferential typing, except the identityMapper object is
6773- // used as a special marker for other purposes.
6777+ /**
6778+ * Detect if the mapper implies an inference context. Specifically, there are 4 possible values
6779+ * for a mapper. Let's go through each one of them:
6780+ *
6781+ * 1. undefined - this means we are not doing inferential typing, but we may do contextual typing,
6782+ * which could cause us to assign a parameter a type
6783+ * 2. identityMapper - means we want to avoid assigning a parameter a type, whether or not we are in
6784+ * inferential typing (context is undefined for the identityMapper)
6785+ * 3. a mapper created by createInferenceMapper - we are doing inferential typing, we want to assign
6786+ * types to parameters and fix type parameters (context is defined)
6787+ * 4. an instantiation mapper created by createTypeMapper or createTypeEraser - this should never be
6788+ * passed as the contextual mapper when checking an expression (context is undefined for these)
6789+ *
6790+ * isInferentialContext is detecting if we are in case 3
6791+ */
67746792 function isInferentialContext(mapper: TypeMapper) {
6775- return mapper && mapper !== identityMapper ;
6793+ return mapper && mapper.context ;
67766794 }
67776795
67786796 // A node is an assignment target if it is on the left hand side of an '=' token, if it is parented by a property
@@ -8861,13 +8879,52 @@ namespace ts {
88618879 let len = signature.parameters.length - (signature.hasRestParameter ? 1 : 0);
88628880 for (let i = 0; i < len; i++) {
88638881 let parameter = signature.parameters[i];
8864- let links = getSymbolLinks(parameter );
8865- links.type = instantiateType(getTypeAtPosition(context, i) , mapper);
8882+ let contextualParameterType = getTypeAtPosition(context, i );
8883+ assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType , mapper);
88668884 }
88678885 if (signature.hasRestParameter && context.hasRestParameter && signature.parameters.length >= context.parameters.length) {
88688886 let parameter = lastOrUndefined(signature.parameters);
8869- let links = getSymbolLinks(parameter);
8870- links.type = instantiateType(getTypeOfSymbol(lastOrUndefined(context.parameters)), mapper);
8887+ let contextualParameterType = getTypeOfSymbol(lastOrUndefined(context.parameters));
8888+ assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType, mapper);
8889+ }
8890+ }
8891+
8892+ function assignTypeToParameterAndFixTypeParameters(parameter: Symbol, contextualType: Type, mapper: TypeMapper) {
8893+ let links = getSymbolLinks(parameter);
8894+ if (!links.type) {
8895+ links.type = instantiateType(contextualType, mapper);
8896+ }
8897+ else if (isInferentialContext(mapper)) {
8898+ // Even if the parameter already has a type, it might be because it was given a type while
8899+ // processing the function as an argument to a prior signature during overload resolution.
8900+ // If this was the case, it may have caused some type parameters to be fixed. So here,
8901+ // we need to ensure that type parameters at the same positions get fixed again. This is
8902+ // done by calling instantiateType to attach the mapper to the contextualType, and then
8903+ // calling inferTypes to force a walk of contextualType so that all the correct fixing
8904+ // happens. The choice to pass in links.type may seem kind of arbitrary, but it serves
8905+ // to make sure that all the correct positions in contextualType are reached by the walk.
8906+ // Here is an example:
8907+ //
8908+ // interface Base {
8909+ // baseProp;
8910+ // }
8911+ // interface Derived extends Base {
8912+ // toBase(): Base;
8913+ // }
8914+ //
8915+ // var derived: Derived;
8916+ //
8917+ // declare function foo<T>(x: T, func: (p: T) => T): T;
8918+ // declare function foo<T>(x: T, func: (p: T) => T): T;
8919+ //
8920+ // var result = foo(derived, d => d.toBase());
8921+ //
8922+ // We are typing d while checking the second overload. But we've already given d
8923+ // a type (Derived) from the first overload. However, we still want to fix the
8924+ // T in the second overload so that we do not infer Base as a candidate for T
8925+ // (inferring Base would make type argument inference inconsistent between the two
8926+ // overloads).
8927+ inferTypes(mapper.context, links.type, instantiateType(contextualType, mapper));
88718928 }
88728929 }
88738930
@@ -9087,27 +9144,36 @@ namespace ts {
90879144
90889145 let links = getNodeLinks(node);
90899146 let type = getTypeOfSymbol(node.symbol);
9090- // Check if function expression is contextually typed and assign parameter types if so
9091- if (!(links.flags & NodeCheckFlags.ContextChecked)) {
9147+ let contextSensitive = isContextSensitive(node);
9148+ let mightFixTypeParameters = contextSensitive && isInferentialContext(contextualMapper);
9149+
9150+ // Check if function expression is contextually typed and assign parameter types if so.
9151+ // See the comment in assignTypeToParameterAndFixTypeParameters to understand why we need to
9152+ // check mightFixTypeParameters.
9153+ if (mightFixTypeParameters || !(links.flags & NodeCheckFlags.ContextChecked)) {
90929154 let contextualSignature = getContextualSignature(node);
90939155 // If a type check is started at a function expression that is an argument of a function call, obtaining the
90949156 // contextual type may recursively get back to here during overload resolution of the call. If so, we will have
90959157 // already assigned contextual types.
9096- if (!(links.flags & NodeCheckFlags.ContextChecked)) {
9158+ let contextChecked = !!(links.flags & NodeCheckFlags.ContextChecked);
9159+ if (mightFixTypeParameters || !contextChecked) {
90979160 links.flags |= NodeCheckFlags.ContextChecked;
90989161 if (contextualSignature) {
90999162 let signature = getSignaturesOfType(type, SignatureKind.Call)[0];
9100- if (isContextSensitive(node) ) {
9163+ if (contextSensitive ) {
91019164 assignContextualParameterTypes(signature, contextualSignature, contextualMapper || identityMapper);
91029165 }
9103- if (!node.type && !signature.resolvedReturnType) {
9166+ if (mightFixTypeParameters || !node.type && !signature.resolvedReturnType) {
91049167 let returnType = getReturnTypeFromBody(node, contextualMapper);
91059168 if (!signature.resolvedReturnType) {
91069169 signature.resolvedReturnType = returnType;
91079170 }
91089171 }
91099172 }
9110- checkSignatureDeclaration(node);
9173+
9174+ if (!contextChecked) {
9175+ checkSignatureDeclaration(node);
9176+ }
91119177 }
91129178 }
91139179
@@ -9797,7 +9863,7 @@ namespace ts {
97979863 }
97989864
97999865 function instantiateTypeWithSingleGenericCallSignature(node: Expression | MethodDeclaration, type: Type, contextualMapper?: TypeMapper) {
9800- if (contextualMapper && contextualMapper !== identityMapper ) {
9866+ if (isInferentialContext( contextualMapper) ) {
98019867 let signature = getSingleCallSignature(type);
98029868 if (signature && signature.typeParameters) {
98039869 let contextualType = getContextualType(<Expression>node);
0 commit comments