Skip to content

Commit 91f8fc6

Browse files
committed
Defer calls to generic functions returning generic functions
1 parent 35ebbec commit 91f8fc6

File tree

1 file changed

+53
-33
lines changed

1 file changed

+53
-33
lines changed

src/compiler/checker.ts

+53-33
Original file line numberDiff line numberDiff line change
@@ -228,9 +228,9 @@ namespace ts {
228228
isContextSensitive,
229229
getFullyQualifiedName,
230230
getResolvedSignature: (node, candidatesOutArray, agumentCount) =>
231-
getResolvedSignatureWorker(node, candidatesOutArray, agumentCount, /*isForSignatureHelp*/ false),
231+
getResolvedSignatureWorker(node, candidatesOutArray, agumentCount, CheckMode.Normal),
232232
getResolvedSignatureForSignatureHelp: (node, candidatesOutArray, agumentCount) =>
233-
getResolvedSignatureWorker(node, candidatesOutArray, agumentCount, /*isForSignatureHelp*/ true),
233+
getResolvedSignatureWorker(node, candidatesOutArray, agumentCount, CheckMode.IsForSignatureHelp),
234234
getConstantValue: nodeIn => {
235235
const node = getParseTreeNode(nodeIn, canHaveConstantValue);
236236
return node ? getConstantValue(node) : undefined;
@@ -374,10 +374,10 @@ namespace ts {
374374
getLocalTypeParametersOfClassOrInterfaceOrTypeAlias,
375375
};
376376

377-
function getResolvedSignatureWorker(nodeIn: CallLikeExpression, candidatesOutArray: Signature[] | undefined, argumentCount: number | undefined, isForSignatureHelp: boolean): Signature | undefined {
377+
function getResolvedSignatureWorker(nodeIn: CallLikeExpression, candidatesOutArray: Signature[] | undefined, argumentCount: number | undefined, checkMode: CheckMode): Signature | undefined {
378378
const node = getParseTreeNode(nodeIn, isCallLikeExpression);
379379
apparentArgumentCount = argumentCount;
380-
const res = node ? getResolvedSignature(node, candidatesOutArray, isForSignatureHelp) : undefined;
380+
const res = node ? getResolvedSignature(node, candidatesOutArray, checkMode) : undefined;
381381
apparentArgumentCount = undefined;
382382
return res;
383383
}
@@ -693,6 +693,7 @@ namespace ts {
693693
Inferential = 1 << 1, // Inferential typing
694694
SkipContextSensitive = 1 << 2, // Skip context sensitive function expressions
695695
SkipGenericFunctions = 1 << 3, // Skip single signature generic functions
696+
IsForSignatureHelp = 1 << 4, // Call resolution for purposes of signature help
696697
}
697698

698699
const enum CallbackCheck {
@@ -20482,7 +20483,7 @@ namespace ts {
2048220483
return createDiagnosticForNodeArray(getSourceFileOfNode(node), typeArguments, Diagnostics.Expected_0_type_arguments_but_got_1, belowArgCount === -Infinity ? aboveArgCount : belowArgCount, argCount);
2048320484
}
2048420485

20485-
function resolveCall(node: CallLikeExpression, signatures: ReadonlyArray<Signature>, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean, fallbackError?: DiagnosticMessage): Signature {
20486+
function resolveCall(node: CallLikeExpression, signatures: ReadonlyArray<Signature>, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode, fallbackError?: DiagnosticMessage): Signature {
2048620487
const isTaggedTemplate = node.kind === SyntaxKind.TaggedTemplateExpression;
2048720488
const isDecorator = node.kind === SyntaxKind.Decorator;
2048820489
const isJsxOpeningOrSelfClosingElement = isJsxOpeningLikeElement(node);
@@ -20555,7 +20556,7 @@ namespace ts {
2055520556
// If we are in signature help, a trailing comma indicates that we intend to provide another argument,
2055620557
// so we will only accept overloads with arity at least 1 higher than the current number of provided arguments.
2055720558
const signatureHelpTrailingComma =
20558-
isForSignatureHelp && node.kind === SyntaxKind.CallExpression && node.arguments.hasTrailingComma;
20559+
!!(checkMode & CheckMode.IsForSignatureHelp) && node.kind === SyntaxKind.CallExpression && node.arguments.hasTrailingComma;
2055920560

2056020561
// Section 4.12.1:
2056120562
// if the candidate list contains one or more signatures for which the type of each argument
@@ -20837,7 +20838,7 @@ namespace ts {
2083720838
return maxParamsIndex;
2083820839
}
2083920840

20840-
function resolveCallExpression(node: CallExpression, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature {
20841+
function resolveCallExpression(node: CallExpression, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode): Signature {
2084120842
if (node.expression.kind === SyntaxKind.SuperKeyword) {
2084220843
const superType = checkSuperExpression(node.expression);
2084320844
if (isTypeAny(superType)) {
@@ -20852,7 +20853,7 @@ namespace ts {
2085220853
const baseTypeNode = getEffectiveBaseTypeNode(getContainingClass(node)!);
2085320854
if (baseTypeNode) {
2085420855
const baseConstructors = getInstantiatedConstructorsForTypeArguments(superType, baseTypeNode.typeArguments, baseTypeNode);
20855-
return resolveCall(node, baseConstructors, candidatesOutArray, isForSignatureHelp);
20856+
return resolveCall(node, baseConstructors, candidatesOutArray, checkMode);
2085620857
}
2085720858
}
2085820859
return resolveUntypedCall(node);
@@ -20912,12 +20913,22 @@ namespace ts {
2091220913
}
2091320914
return resolveErrorCall(node);
2091420915
}
20916+
// If we are skipping generic functions (i.e. this call is an argument to another call for which context
20917+
// sensitive arguments are being deferred) and every call signature is generic and returns a function type,
20918+
// we return resolvingSignature here. This result will be propagated out and turned into anyFunctionType.
20919+
if (checkMode & CheckMode.SkipGenericFunctions && callSignatures.every(isGenericFunctionReturningFunction)) {
20920+
return resolvingSignature;
20921+
}
2091520922
// If the function is explicitly marked with `@class`, then it must be constructed.
2091620923
if (callSignatures.some(sig => isInJSFile(sig.declaration) && !!getJSDocClassTag(sig.declaration!))) {
2091720924
error(node, Diagnostics.Value_of_type_0_is_not_callable_Did_you_mean_to_include_new, typeToString(funcType));
2091820925
return resolveErrorCall(node);
2091920926
}
20920-
return resolveCall(node, callSignatures, candidatesOutArray, isForSignatureHelp);
20927+
return resolveCall(node, callSignatures, candidatesOutArray, checkMode);
20928+
}
20929+
20930+
function isGenericFunctionReturningFunction(signature: Signature) {
20931+
return !!(signature.typeParameters && getSingleCallSignature(getReturnTypeOfSignature(signature)));
2092120932
}
2092220933

2092320934
/**
@@ -20931,7 +20942,7 @@ namespace ts {
2093120942
!numCallSignatures && !numConstructSignatures && !(apparentFuncType.flags & (TypeFlags.Union | TypeFlags.Never)) && isTypeAssignableTo(funcType, globalFunctionType);
2093220943
}
2093320944

20934-
function resolveNewExpression(node: NewExpression, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature {
20945+
function resolveNewExpression(node: NewExpression, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode): Signature {
2093520946
if (node.arguments && languageVersion < ScriptTarget.ES5) {
2093620947
const spreadIndex = getSpreadArgumentIndex(node.arguments);
2093720948
if (spreadIndex >= 0) {
@@ -20984,7 +20995,7 @@ namespace ts {
2098420995
return resolveErrorCall(node);
2098520996
}
2098620997

20987-
return resolveCall(node, constructSignatures, candidatesOutArray, isForSignatureHelp);
20998+
return resolveCall(node, constructSignatures, candidatesOutArray, checkMode);
2098820999
}
2098921000

2099021001
// If expressionType's apparent type is an object type with no construct signatures but
@@ -20993,7 +21004,7 @@ namespace ts {
2099321004
// operation is Any. It is an error to have a Void this type.
2099421005
const callSignatures = getSignaturesOfType(expressionType, SignatureKind.Call);
2099521006
if (callSignatures.length) {
20996-
const signature = resolveCall(node, callSignatures, candidatesOutArray, isForSignatureHelp);
21007+
const signature = resolveCall(node, callSignatures, candidatesOutArray, checkMode);
2099721008
if (!noImplicitAny) {
2099821009
if (signature.declaration && !isJSConstructor(signature.declaration) && getReturnTypeOfSignature(signature) !== voidType) {
2099921010
error(node, Diagnostics.Only_a_void_function_can_be_called_with_the_new_keyword);
@@ -21103,7 +21114,7 @@ namespace ts {
2110321114
}
2110421115
}
2110521116

21106-
function resolveTaggedTemplateExpression(node: TaggedTemplateExpression, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature {
21117+
function resolveTaggedTemplateExpression(node: TaggedTemplateExpression, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode): Signature {
2110721118
const tagType = checkExpression(node.tag);
2110821119
const apparentType = getApparentType(tagType);
2110921120

@@ -21124,7 +21135,7 @@ namespace ts {
2112421135
return resolveErrorCall(node);
2112521136
}
2112621137

21127-
return resolveCall(node, callSignatures, candidatesOutArray, isForSignatureHelp);
21138+
return resolveCall(node, callSignatures, candidatesOutArray, checkMode);
2112821139
}
2112921140

2113021141
/**
@@ -21155,7 +21166,7 @@ namespace ts {
2115521166
/**
2115621167
* Resolves a decorator as if it were a call expression.
2115721168
*/
21158-
function resolveDecorator(node: Decorator, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature {
21169+
function resolveDecorator(node: Decorator, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode): Signature {
2115921170
const funcType = checkExpression(node.expression);
2116021171
const apparentType = getApparentType(funcType);
2116121172
if (apparentType === errorType) {
@@ -21184,7 +21195,7 @@ namespace ts {
2118421195
return resolveErrorCall(node);
2118521196
}
2118621197

21187-
return resolveCall(node, callSignatures, candidatesOutArray, isForSignatureHelp, headMessage);
21198+
return resolveCall(node, callSignatures, candidatesOutArray, checkMode, headMessage);
2118821199
}
2118921200

2119021201
function createSignatureForJSXIntrinsic(node: JsxOpeningLikeElement, result: Type): Signature {
@@ -21213,7 +21224,7 @@ namespace ts {
2121321224
);
2121421225
}
2121521226

21216-
function resolveJsxOpeningLikeElement(node: JsxOpeningLikeElement, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature {
21227+
function resolveJsxOpeningLikeElement(node: JsxOpeningLikeElement, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode): Signature {
2121721228
if (isJsxIntrinsicIdentifier(node.tagName)) {
2121821229
const result = getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node);
2121921230
const fakeSignature = createSignatureForJSXIntrinsic(node, result);
@@ -21237,7 +21248,7 @@ namespace ts {
2123721248
return resolveErrorCall(node);
2123821249
}
2123921250

21240-
return resolveCall(node, signatures, candidatesOutArray, isForSignatureHelp);
21251+
return resolveCall(node, signatures, candidatesOutArray, checkMode);
2124121252
}
2124221253

2124321254
/**
@@ -21252,19 +21263,19 @@ namespace ts {
2125221263
signature.parameters.length < getDecoratorArgumentCount(decorator, signature));
2125321264
}
2125421265

21255-
function resolveSignature(node: CallLikeExpression, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature {
21266+
function resolveSignature(node: CallLikeExpression, candidatesOutArray: Signature[] | undefined, checkMode: CheckMode): Signature {
2125621267
switch (node.kind) {
2125721268
case SyntaxKind.CallExpression:
21258-
return resolveCallExpression(node, candidatesOutArray, isForSignatureHelp);
21269+
return resolveCallExpression(node, candidatesOutArray, checkMode);
2125921270
case SyntaxKind.NewExpression:
21260-
return resolveNewExpression(node, candidatesOutArray, isForSignatureHelp);
21271+
return resolveNewExpression(node, candidatesOutArray, checkMode);
2126121272
case SyntaxKind.TaggedTemplateExpression:
21262-
return resolveTaggedTemplateExpression(node, candidatesOutArray, isForSignatureHelp);
21273+
return resolveTaggedTemplateExpression(node, candidatesOutArray, checkMode);
2126321274
case SyntaxKind.Decorator:
21264-
return resolveDecorator(node, candidatesOutArray, isForSignatureHelp);
21275+
return resolveDecorator(node, candidatesOutArray, checkMode);
2126521276
case SyntaxKind.JsxOpeningElement:
2126621277
case SyntaxKind.JsxSelfClosingElement:
21267-
return resolveJsxOpeningLikeElement(node, candidatesOutArray, isForSignatureHelp);
21278+
return resolveJsxOpeningLikeElement(node, candidatesOutArray, checkMode);
2126821279
}
2126921280
throw Debug.assertNever(node, "Branch in 'resolveSignature' should be unreachable.");
2127021281
}
@@ -21276,7 +21287,7 @@ namespace ts {
2127621287
* the function will fill it up with appropriate candidate signatures
2127721288
* @return a signature of the call-like expression or undefined if one can't be found
2127821289
*/
21279-
function getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[] | undefined, isForSignatureHelp = false): Signature {
21290+
function getResolvedSignature(node: CallLikeExpression, candidatesOutArray?: Signature[] | undefined, checkMode?: CheckMode): Signature {
2128021291
const links = getNodeLinks(node);
2128121292
// If getResolvedSignature has already been called, we will have cached the resolvedSignature.
2128221293
// However, it is possible that either candidatesOutArray was not passed in the first time,
@@ -21287,11 +21298,15 @@ namespace ts {
2128721298
return cached;
2128821299
}
2128921300
links.resolvedSignature = resolvingSignature;
21290-
const result = resolveSignature(node, candidatesOutArray, isForSignatureHelp);
21291-
// If signature resolution originated in control flow type analysis (for example to compute the
21292-
// assigned type in a flow assignment) we don't cache the result as it may be based on temporary
21293-
// types from the control flow analysis.
21294-
links.resolvedSignature = flowLoopStart === flowLoopCount ? result : cached;
21301+
const result = resolveSignature(node, candidatesOutArray, checkMode || CheckMode.Normal);
21302+
// When CheckMode.SkipGenericFunctions is set we use resolvingSignature to indicate that call
21303+
// resolution should be deferred.
21304+
if (result !== resolvingSignature) {
21305+
// If signature resolution originated in control flow type analysis (for example to compute the
21306+
// assigned type in a flow assignment) we don't cache the result as it may be based on temporary
21307+
// types from the control flow analysis.
21308+
links.resolvedSignature = flowLoopStart === flowLoopCount ? result : cached;
21309+
}
2129521310
return result;
2129621311
}
2129721312

@@ -21375,10 +21390,15 @@ namespace ts {
2137521390
* @param node The call/new expression to be checked.
2137621391
* @returns On success, the expression's signature's return type. On failure, anyType.
2137721392
*/
21378-
function checkCallExpression(node: CallExpression | NewExpression): Type {
21393+
function checkCallExpression(node: CallExpression | NewExpression, checkMode?: CheckMode): Type {
2137921394
if (!checkGrammarTypeArguments(node, node.typeArguments)) checkGrammarArguments(node.arguments);
2138021395

21381-
const signature = getResolvedSignature(node);
21396+
const signature = getResolvedSignature(node, /*candidatesOutArray*/ undefined, checkMode);
21397+
if (signature === resolvingSignature) {
21398+
// CheckMode.SkipGenericFunctions is enabled and this is a call to a generic function that
21399+
// returns a function type. We defer checking and return anyFunctionType.
21400+
return anyFunctionType;
21401+
}
2138221402

2138321403
if (node.expression.kind === SyntaxKind.SuperKeyword) {
2138421404
return voidType;
@@ -23505,7 +23525,7 @@ namespace ts {
2350523525
}
2350623526
/* falls through */
2350723527
case SyntaxKind.NewExpression:
23508-
return checkCallExpression(<CallExpression>node);
23528+
return checkCallExpression(<CallExpression>node, checkMode);
2350923529
case SyntaxKind.TaggedTemplateExpression:
2351023530
return checkTaggedTemplateExpression(<TaggedTemplateExpression>node);
2351123531
case SyntaxKind.ParenthesizedExpression:

0 commit comments

Comments
 (0)