@@ -4249,15 +4249,18 @@ namespace ts {
4249
4249
}
4250
4250
4251
4251
function createInferenceMapper(context: InferenceContext): TypeMapper {
4252
- return t => {
4252
+ let mapper: TypeMapper = t => {
4253
4253
for (let i = 0; i < context.typeParameters.length; i++) {
4254
4254
if (t === context.typeParameters[i]) {
4255
4255
context.inferences[i].isFixed = true;
4256
4256
return getInferredType(context, i);
4257
4257
}
4258
4258
}
4259
- return t;
4259
+ return t;
4260
4260
};
4261
+
4262
+ mapper.context = context;
4263
+ return mapper;
4261
4264
}
4262
4265
4263
4266
function identityMapper(type: Type): Type {
@@ -5468,7 +5471,9 @@ namespace ts {
5468
5471
function createInferenceContext(typeParameters: TypeParameter[], inferUnionTypes: boolean): InferenceContext {
5469
5472
let inferences: TypeInferences[] = [];
5470
5473
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
+ });
5472
5477
}
5473
5478
return {
5474
5479
typeParameters,
@@ -6769,10 +6774,23 @@ namespace ts {
6769
6774
return result;
6770
6775
}
6771
6776
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
+ */
6774
6792
function isInferentialContext(mapper: TypeMapper) {
6775
- return mapper && mapper !== identityMapper ;
6793
+ return mapper && mapper.context ;
6776
6794
}
6777
6795
6778
6796
// 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 {
8861
8879
let len = signature.parameters.length - (signature.hasRestParameter ? 1 : 0);
8862
8880
for (let i = 0; i < len; i++) {
8863
8881
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);
8866
8884
}
8867
8885
if (signature.hasRestParameter && context.hasRestParameter && signature.parameters.length >= context.parameters.length) {
8868
8886
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));
8871
8928
}
8872
8929
}
8873
8930
@@ -9087,27 +9144,36 @@ namespace ts {
9087
9144
9088
9145
let links = getNodeLinks(node);
9089
9146
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)) {
9092
9154
let contextualSignature = getContextualSignature(node);
9093
9155
// If a type check is started at a function expression that is an argument of a function call, obtaining the
9094
9156
// contextual type may recursively get back to here during overload resolution of the call. If so, we will have
9095
9157
// already assigned contextual types.
9096
- if (!(links.flags & NodeCheckFlags.ContextChecked)) {
9158
+ let contextChecked = !!(links.flags & NodeCheckFlags.ContextChecked);
9159
+ if (mightFixTypeParameters || !contextChecked) {
9097
9160
links.flags |= NodeCheckFlags.ContextChecked;
9098
9161
if (contextualSignature) {
9099
9162
let signature = getSignaturesOfType(type, SignatureKind.Call)[0];
9100
- if (isContextSensitive(node) ) {
9163
+ if (contextSensitive ) {
9101
9164
assignContextualParameterTypes(signature, contextualSignature, contextualMapper || identityMapper);
9102
9165
}
9103
- if (!node.type && !signature.resolvedReturnType) {
9166
+ if (mightFixTypeParameters || !node.type && !signature.resolvedReturnType) {
9104
9167
let returnType = getReturnTypeFromBody(node, contextualMapper);
9105
9168
if (!signature.resolvedReturnType) {
9106
9169
signature.resolvedReturnType = returnType;
9107
9170
}
9108
9171
}
9109
9172
}
9110
- checkSignatureDeclaration(node);
9173
+
9174
+ if (!contextChecked) {
9175
+ checkSignatureDeclaration(node);
9176
+ }
9111
9177
}
9112
9178
}
9113
9179
@@ -9797,7 +9863,7 @@ namespace ts {
9797
9863
}
9798
9864
9799
9865
function instantiateTypeWithSingleGenericCallSignature(node: Expression | MethodDeclaration, type: Type, contextualMapper?: TypeMapper) {
9800
- if (contextualMapper && contextualMapper !== identityMapper ) {
9866
+ if (isInferentialContext( contextualMapper) ) {
9801
9867
let signature = getSingleCallSignature(type);
9802
9868
if (signature && signature.typeParameters) {
9803
9869
let contextualType = getContextualType(<Expression>node);
0 commit comments