Skip to content

Commit 872e916

Browse files
committed
Infer generic type for lambda with generic contextual signature
1 parent 50b24ee commit 872e916

File tree

1 file changed

+23
-46
lines changed

1 file changed

+23
-46
lines changed

src/compiler/checker.ts

Lines changed: 23 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -8038,7 +8038,7 @@ namespace ts {
80388038
const result = createSignature(signature.declaration, freshTypeParameters,
80398039
signature.thisParameter && instantiateSymbol(signature.thisParameter, mapper),
80408040
instantiateList(signature.parameters, mapper, instantiateSymbol),
8041-
instantiateType(signature.resolvedReturnType, mapper),
8041+
/*resolvedReturnType*/ undefined,
80428042
freshTypePredicate,
80438043
signature.minArgumentCount, signature.hasRestParameter, signature.hasLiteralTypes);
80448044
result.target = signature;
@@ -16347,37 +16347,40 @@ namespace ts {
1634716347
return signature.parameters.length > 0 ? getTypeAtPosition(signature, 0) : neverType;
1634816348
}
1634916349

16350-
function assignContextualParameterTypes(signature: Signature, context: Signature, mapper: TypeMapper, checkMode: CheckMode) {
16350+
function inferFromAnnotatedParameters(signature: Signature, context: Signature, mapper: TypeMapper) {
1635116351
const len = signature.parameters.length - (signature.hasRestParameter ? 1 : 0);
16352-
if (checkMode === CheckMode.Inferential) {
16353-
for (let i = 0; i < len; i++) {
16354-
const declaration = <ParameterDeclaration>signature.parameters[i].valueDeclaration;
16355-
if (declaration.type) {
16356-
inferTypes((<InferenceContext>mapper).inferences, getTypeFromTypeNode(declaration.type), getTypeAtPosition(context, i));
16357-
}
16352+
for (let i = 0; i < len; i++) {
16353+
const declaration = <ParameterDeclaration>signature.parameters[i].valueDeclaration;
16354+
if (declaration.type) {
16355+
inferTypes((<InferenceContext>mapper).inferences, getTypeFromTypeNode(declaration.type), getTypeAtPosition(context, i));
1635816356
}
1635916357
}
16358+
}
16359+
16360+
function assignContextualParameterTypes(signature: Signature, context: Signature) {
16361+
signature.typeParameters = context.typeParameters;
1636016362
if (context.thisParameter) {
1636116363
const parameter = signature.thisParameter;
1636216364
if (!parameter || parameter.valueDeclaration && !(<ParameterDeclaration>parameter.valueDeclaration).type) {
1636316365
if (!parameter) {
1636416366
signature.thisParameter = createSymbolWithType(context.thisParameter, /*type*/ undefined);
1636516367
}
16366-
assignTypeToParameterAndFixTypeParameters(signature.thisParameter, getTypeOfSymbol(context.thisParameter), mapper, checkMode);
16368+
assignTypeToParameterAndFixTypeParameters(signature.thisParameter, getTypeOfSymbol(context.thisParameter));
1636716369
}
1636816370
}
16371+
const len = signature.parameters.length - (signature.hasRestParameter ? 1 : 0);
1636916372
for (let i = 0; i < len; i++) {
1637016373
const parameter = signature.parameters[i];
1637116374
if (!(<ParameterDeclaration>parameter.valueDeclaration).type) {
1637216375
const contextualParameterType = getTypeAtPosition(context, i);
16373-
assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType, mapper, checkMode);
16376+
assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType);
1637416377
}
1637516378
}
1637616379
if (signature.hasRestParameter && isRestParameterIndex(context, signature.parameters.length - 1)) {
1637716380
const parameter = lastOrUndefined(signature.parameters);
1637816381
if (!(<ParameterDeclaration>parameter.valueDeclaration).type) {
1637916382
const contextualParameterType = getTypeOfSymbol(lastOrUndefined(context.parameters));
16380-
assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType, mapper, checkMode);
16383+
assignTypeToParameterAndFixTypeParameters(parameter, contextualParameterType);
1638116384
}
1638216385
}
1638316386
}
@@ -16397,10 +16400,10 @@ namespace ts {
1639716400
}
1639816401
}
1639916402

16400-
function assignTypeToParameterAndFixTypeParameters(parameter: Symbol, contextualType: Type, mapper: TypeMapper, checkMode: CheckMode) {
16403+
function assignTypeToParameterAndFixTypeParameters(parameter: Symbol, contextualType: Type) {
1640116404
const links = getSymbolLinks(parameter);
1640216405
if (!links.type) {
16403-
links.type = instantiateType(contextualType, mapper);
16406+
links.type = contextualType;
1640416407
const name = getNameOfDeclaration(parameter.valueDeclaration);
1640516408
// if inference didn't come up with anything but {}, fall back to the binding pattern if present.
1640616409
if (links.type === emptyObjectType &&
@@ -16409,38 +16412,6 @@ namespace ts {
1640916412
}
1641016413
assignBindingElementTypes(<ParameterDeclaration>parameter.valueDeclaration);
1641116414
}
16412-
else if (checkMode === CheckMode.Inferential) {
16413-
// Even if the parameter already has a type, it might be because it was given a type while
16414-
// processing the function as an argument to a prior signature during overload resolution.
16415-
// If this was the case, it may have caused some type parameters to be fixed. So here,
16416-
// we need to ensure that type parameters at the same positions get fixed again. This is
16417-
// done by calling instantiateType to attach the mapper to the contextualType, and then
16418-
// calling inferTypes to force a walk of contextualType so that all the correct fixing
16419-
// happens. The choice to pass in links.type may seem kind of arbitrary, but it serves
16420-
// to make sure that all the correct positions in contextualType are reached by the walk.
16421-
// Here is an example:
16422-
//
16423-
// interface Base {
16424-
// baseProp;
16425-
// }
16426-
// interface Derived extends Base {
16427-
// toBase(): Base;
16428-
// }
16429-
//
16430-
// var derived: Derived;
16431-
//
16432-
// declare function foo<T>(x: T, func: (p: T) => T): T;
16433-
// declare function foo<T>(x: T, func: (p: T) => T): T;
16434-
//
16435-
// var result = foo(derived, d => d.toBase());
16436-
//
16437-
// We are typing d while checking the second overload. But we've already given d
16438-
// a type (Derived) from the first overload. However, we still want to fix the
16439-
// T in the second overload so that we do not infer Base as a candidate for T
16440-
// (inferring Base would make type argument inference inconsistent between the two
16441-
// overloads).
16442-
inferTypes((<InferenceContext>mapper).inferences, links.type, instantiateType(contextualType, mapper));
16443-
}
1644416415
}
1644516416

1644616417
function getReturnTypeFromJSDocComment(func: SignatureDeclaration | FunctionDeclaration): Type {
@@ -16739,7 +16710,13 @@ namespace ts {
1673916710
if (contextualSignature) {
1674016711
const signature = getSignaturesOfType(type, SignatureKind.Call)[0];
1674116712
if (contextSensitive) {
16742-
assignContextualParameterTypes(signature, contextualSignature, getContextualMapper(node), checkMode);
16713+
const contextualMapper = getContextualMapper(node);
16714+
if (checkMode === CheckMode.Inferential) {
16715+
inferFromAnnotatedParameters(signature, contextualSignature, contextualMapper);
16716+
}
16717+
const instantiatedContextualSignature = contextualMapper === identityMapper ?
16718+
contextualSignature : instantiateSignature(contextualSignature, contextualMapper);
16719+
assignContextualParameterTypes(signature, instantiatedContextualSignature);
1674316720
}
1674416721
if (mightFixTypeParameters || !node.type && !signature.resolvedReturnType) {
1674516722
const returnType = getReturnTypeFromBody(node, checkMode);

0 commit comments

Comments
 (0)