Skip to content

Commit 544b177

Browse files
committed
Fix type parameters every time a parameter is assigned a contextual type
1 parent 42bff6b commit 544b177

File tree

2 files changed

+57
-17
lines changed

2 files changed

+57
-17
lines changed

src/compiler/checker.ts

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4225,7 +4225,7 @@ namespace ts {
42254225
}
42264226

42274227
function createInferenceMapper(context: InferenceContext): TypeMapper {
4228-
return t => {
4228+
let mapper: TypeMapper = t => {
42294229
for (let i = 0; i < context.typeParameters.length; i++) {
42304230
if (t === context.typeParameters[i]) {
42314231
context.inferences[i].isFixed = true;
@@ -4234,6 +4234,20 @@ namespace ts {
42344234
}
42354235
return t;
42364236
}
4237+
4238+
mapper.context = context;
4239+
return mapper;
4240+
}
4241+
4242+
function fixTypeParametersAfterInferringFromContextualParameterTypes(context: InferenceContext): void {
4243+
for (let i = 0; i < context.typeParameters.length; i++) {
4244+
let typeParameterInfo = context.inferences[i];
4245+
if (typeParameterInfo.fixAfterInferringFromContextualParameterType) {
4246+
typeParameterInfo.fixAfterInferringFromContextualParameterType = false;
4247+
typeParameterInfo.isFixed = true;
4248+
getInferredType(context, i);
4249+
}
4250+
}
42374251
}
42384252

42394253
function identityMapper(type: Type): Type {
@@ -5397,7 +5411,10 @@ namespace ts {
53975411
function createInferenceContext(typeParameters: TypeParameter[], inferUnionTypes: boolean): InferenceContext {
53985412
let inferences: TypeInferences[] = [];
53995413
for (let unused of typeParameters) {
5400-
inferences.push({ primary: undefined, secondary: undefined, isFixed: false });
5414+
inferences.push({
5415+
primary: undefined, secondary: undefined,
5416+
isFixed: false, fixAfterInferringFromContextualParameterType: false
5417+
});
54015418
}
54025419
return {
54035420
typeParameters,
@@ -5407,7 +5424,7 @@ namespace ts {
54075424
};
54085425
}
54095426

5410-
function inferTypes(context: InferenceContext, source: Type, target: Type) {
5427+
function inferTypes(context: InferenceContext, source: Type, target: Type, inferringFromContextuallyTypedParameter: boolean) {
54115428
let sourceStack: Type[];
54125429
let targetStack: Type[];
54135430
let depth = 0;
@@ -5446,6 +5463,9 @@ namespace ts {
54465463
if (!contains(candidates, source)) {
54475464
candidates.push(source);
54485465
}
5466+
if (inferringFromContextuallyTypedParameter) {
5467+
inferences.fixAfterInferringFromContextualParameterType = true;
5468+
}
54495469
}
54505470
return;
54515471
}
@@ -6698,7 +6718,7 @@ namespace ts {
66986718
// Presence of a contextual type mapper indicates inferential typing, except the identityMapper object is
66996719
// used as a special marker for other purposes.
67006720
function isInferentialContext(mapper: TypeMapper) {
6701-
return mapper && mapper !== identityMapper;
6721+
return mapper && mapper.context;
67026722
}
67036723

67046724
// A node is an assignment target if it is on the left hand side of an '=' token, if it is parented by a property
@@ -7834,7 +7854,7 @@ namespace ts {
78347854
let context = createInferenceContext(signature.typeParameters, /*inferUnionTypes*/ true);
78357855
forEachMatchingParameterType(contextualSignature, signature, (source, target) => {
78367856
// Type parameters from outer context referenced by source type are fixed by instantiation of the source type
7837-
inferTypes(context, instantiateType(source, contextualMapper), target);
7857+
inferTypes(context, instantiateType(source, contextualMapper), target, false);
78387858
});
78397859
return getSignatureInstantiation(signature, getInferredTypes(context));
78407860
}
@@ -7884,7 +7904,7 @@ namespace ts {
78847904
argType = checkExpressionWithContextualType(arg, paramType, mapper);
78857905
}
78867906

7887-
inferTypes(context, argType, paramType);
7907+
inferTypes(context, argType, paramType, false);
78887908
}
78897909
}
78907910

@@ -7899,7 +7919,7 @@ namespace ts {
78997919
if (excludeArgument[i] === false) {
79007920
let arg = args[i];
79017921
let paramType = getTypeAtPosition(signature, i);
7902-
inferTypes(context, checkExpressionWithContextualType(arg, paramType, inferenceMapper), paramType);
7922+
inferTypes(context, checkExpressionWithContextualType(arg, paramType, inferenceMapper), paramType, false);
79037923
}
79047924
}
79057925
}
@@ -8788,13 +8808,23 @@ namespace ts {
87888808
let len = signature.parameters.length - (signature.hasRestParameter ? 1 : 0);
87898809
for (let i = 0; i < len; i++) {
87908810
let parameter = signature.parameters[i];
8791-
let links = getSymbolLinks(parameter);
8792-
links.type = instantiateType(getTypeAtPosition(context, i), mapper);
8811+
let contextualParameterType = getTypeAtPosition(context, i);
8812+
assignTypeToParameterAndFixTypeParameters(getSymbolLinks(parameter), contextualParameterType, mapper);
87938813
}
87948814
if (signature.hasRestParameter && context.hasRestParameter && signature.parameters.length >= context.parameters.length) {
87958815
let parameter = lastOrUndefined(signature.parameters);
8796-
let links = getSymbolLinks(parameter);
8797-
links.type = instantiateType(getTypeOfSymbol(lastOrUndefined(context.parameters)), mapper);
8816+
let contextualParameterType = getTypeOfSymbol(lastOrUndefined(context.parameters));
8817+
assignTypeToParameterAndFixTypeParameters(getSymbolLinks(parameter), contextualParameterType, mapper);
8818+
}
8819+
}
8820+
8821+
function assignTypeToParameterAndFixTypeParameters(parameterLinks: SymbolLinks, contextualType: Type, mapper: TypeMapper) {
8822+
if (!parameterLinks.type) {
8823+
parameterLinks.type = instantiateType(contextualType, mapper);
8824+
}
8825+
else if (isInferentialContext(mapper)) {
8826+
inferTypes(mapper.context, parameterLinks.type, contextualType, true);
8827+
fixTypeParametersAfterInferringFromContextualParameterTypes(mapper.context);
87988828
}
87998829
}
88008830

@@ -9014,27 +9044,34 @@ namespace ts {
90149044

90159045
let links = getNodeLinks(node);
90169046
let type = getTypeOfSymbol(node.symbol);
9047+
let contextSensitive = isContextSensitive(node);
9048+
let mightFixTypeParameters = contextSensitive && isInferentialContext(contextualMapper);
9049+
90179050
// Check if function expression is contextually typed and assign parameter types if so
9018-
if (!(links.flags & NodeCheckFlags.ContextChecked)) {
9051+
if (mightFixTypeParameters || !(links.flags & NodeCheckFlags.ContextChecked)) {
90199052
let contextualSignature = getContextualSignature(node);
90209053
// If a type check is started at a function expression that is an argument of a function call, obtaining the
90219054
// contextual type may recursively get back to here during overload resolution of the call. If so, we will have
90229055
// already assigned contextual types.
9023-
if (!(links.flags & NodeCheckFlags.ContextChecked)) {
9056+
let contextChecked = !!(links.flags & NodeCheckFlags.ContextChecked);
9057+
if (mightFixTypeParameters || !contextChecked) {
90249058
links.flags |= NodeCheckFlags.ContextChecked;
90259059
if (contextualSignature) {
90269060
let signature = getSignaturesOfType(type, SignatureKind.Call)[0];
9027-
if (isContextSensitive(node)) {
9061+
if (contextSensitive) {
90289062
assignContextualParameterTypes(signature, contextualSignature, contextualMapper || identityMapper);
90299063
}
9030-
if (!node.type && !signature.resolvedReturnType) {
9064+
if (mightFixTypeParameters || !node.type && !signature.resolvedReturnType) {
90319065
let returnType = getReturnTypeFromBody(node, contextualMapper);
90329066
if (!signature.resolvedReturnType) {
90339067
signature.resolvedReturnType = returnType;
90349068
}
90359069
}
90369070
}
9037-
checkSignatureDeclaration(node);
9071+
9072+
if (!contextChecked) {
9073+
checkSignatureDeclaration(node);
9074+
}
90389075
}
90399076
}
90409077

@@ -9724,7 +9761,7 @@ namespace ts {
97249761
}
97259762

97269763
function instantiateTypeWithSingleGenericCallSignature(node: Expression | MethodDeclaration, type: Type, contextualMapper?: TypeMapper) {
9727-
if (contextualMapper && contextualMapper !== identityMapper) {
9764+
if (isInferentialContext(contextualMapper)) {
97289765
let signature = getSingleCallSignature(type);
97299766
if (signature && signature.typeParameters) {
97309767
let contextualType = getContextualType(<Expression>node);

src/compiler/types.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1893,6 +1893,7 @@ namespace ts {
18931893
/* @internal */
18941894
export interface TypeMapper {
18951895
(t: TypeParameter): Type;
1896+
context?: InferenceContext;
18961897
}
18971898

18981899
/* @internal */
@@ -1901,6 +1902,8 @@ namespace ts {
19011902
secondary: Type[]; // Inferences made to a type parameter in a union type
19021903
isFixed: boolean; // Whether the type parameter is fixed, as defined in section 4.12.2 of the TypeScript spec
19031904
// If a type parameter is fixed, no more inferences can be made for the type parameter
1905+
1906+
fixAfterInferringFromContextualParameterType: boolean;
19041907
}
19051908

19061909
/* @internal */

0 commit comments

Comments
 (0)