Skip to content

Commit 0a59da1

Browse files
author
Andy
authored
In pickLongestCandidateSignature, instantiate using inferred type arguments (microsoft#26646)
1 parent 6419240 commit 0a59da1

File tree

5 files changed

+49
-22
lines changed

5 files changed

+49
-22
lines changed

src/compiler/checker.ts

+34-19
Original file line numberDiff line numberDiff line change
@@ -18638,7 +18638,7 @@ namespace ts {
1863818638
return getInferredTypes(context);
1863918639
}
1864018640

18641-
function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: ReadonlyArray<Expression>, excludeArgument: boolean[] | undefined, context: InferenceContext): Type[] {
18641+
function inferTypeArguments(node: CallLikeExpression, signature: Signature, args: ReadonlyArray<Expression>, excludeArgument: ReadonlyArray<boolean> | undefined, context: InferenceContext): Type[] {
1864218642
// Clear out all the inference results from the last time inferTypeArguments was called on this context
1864318643
for (const inference of context.inferences) {
1864418644
// As an optimization, we don't have to clear (and later recompute) inferred types
@@ -19052,19 +19052,7 @@ namespace ts {
1905219052
// For a decorator, no arguments are susceptible to contextual typing due to the fact
1905319053
// decorators are applied to a declaration by the emitter, and not to an expression.
1905419054
const isSingleNonGenericCandidate = candidates.length === 1 && !candidates[0].typeParameters;
19055-
let excludeArgument: boolean[] | undefined;
19056-
if (!isDecorator && !isSingleNonGenericCandidate) {
19057-
// We do not need to call `getEffectiveArgumentCount` here as it only
19058-
// applies when calculating the number of arguments for a decorator.
19059-
for (let i = 0; i < args.length; i++) {
19060-
if (isContextSensitive(args[i])) {
19061-
if (!excludeArgument) {
19062-
excludeArgument = new Array(args.length);
19063-
}
19064-
excludeArgument[i] = true;
19065-
}
19066-
}
19067-
}
19055+
let excludeArgument = !isDecorator && !isSingleNonGenericCandidate ? getExcludeArgument(args) : undefined;
1906819056

1906919057
// The following variables are captured and modified by calls to chooseOverload.
1907019058
// If overload resolution or type argument inference fails, we want to report the
@@ -19227,6 +19215,21 @@ namespace ts {
1922719215
}
1922819216
}
1922919217

19218+
function getExcludeArgument(args: ReadonlyArray<Expression>): boolean[] | undefined {
19219+
let excludeArgument: boolean[] | undefined;
19220+
// We do not need to call `getEffectiveArgumentCount` here as it only
19221+
// applies when calculating the number of arguments for a decorator.
19222+
for (let i = 0; i < args.length; i++) {
19223+
if (isContextSensitive(args[i])) {
19224+
if (!excludeArgument) {
19225+
excludeArgument = new Array(args.length);
19226+
}
19227+
excludeArgument[i] = true;
19228+
}
19229+
}
19230+
return excludeArgument;
19231+
}
19232+
1923019233
// No signature was applicable. We have already reported the errors for the invalid signature.
1923119234
// If this is a type resolution session, e.g. Language Service, try to get better information than anySignature.
1923219235
function getCandidateForOverloadFailure(
@@ -19305,17 +19308,29 @@ namespace ts {
1930519308
return candidate;
1930619309
}
1930719310

19308-
const typeArgumentNodes: ReadonlyArray<TypeNode> = callLikeExpressionMayHaveTypeArguments(node) ? node.typeArguments || emptyArray : emptyArray;
19311+
const typeArgumentNodes: ReadonlyArray<TypeNode> | undefined = callLikeExpressionMayHaveTypeArguments(node) ? node.typeArguments : undefined;
19312+
const instantiated = typeArgumentNodes
19313+
? createSignatureInstantiation(candidate, getTypeArgumentsFromNodes(typeArgumentNodes, typeParameters, isInJavaScriptFile(node)))
19314+
: inferSignatureInstantiationForOverloadFailure(node, typeParameters, candidate, args);
19315+
candidates[bestIndex] = instantiated;
19316+
return instantiated;
19317+
}
19318+
19319+
function getTypeArgumentsFromNodes(typeArgumentNodes: ReadonlyArray<TypeNode>, typeParameters: ReadonlyArray<TypeParameter>, isJs: boolean): ReadonlyArray<Type> {
1930919320
const typeArguments = typeArgumentNodes.map(getTypeOfNode);
1931019321
while (typeArguments.length > typeParameters.length) {
1931119322
typeArguments.pop();
1931219323
}
1931319324
while (typeArguments.length < typeParameters.length) {
19314-
typeArguments.push(getConstraintOfTypeParameter(typeParameters[typeArguments.length]) || getDefaultTypeArgumentType(isInJavaScriptFile(node)));
19325+
typeArguments.push(getConstraintOfTypeParameter(typeParameters[typeArguments.length]) || getDefaultTypeArgumentType(isJs));
1931519326
}
19316-
const instantiated = createSignatureInstantiation(candidate, typeArguments);
19317-
candidates[bestIndex] = instantiated;
19318-
return instantiated;
19327+
return typeArguments;
19328+
}
19329+
19330+
function inferSignatureInstantiationForOverloadFailure(node: CallLikeExpression, typeParameters: ReadonlyArray<TypeParameter>, candidate: Signature, args: ReadonlyArray<Expression>): Signature {
19331+
const inferenceContext = createInferenceContext(typeParameters, candidate, /*flags*/ isInJavaScriptFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None);
19332+
const typeArgumentTypes = inferTypeArguments(node, candidate, args, getExcludeArgument(args), inferenceContext);
19333+
return createSignatureInstantiation(candidate, typeArgumentTypes);
1931919334
}
1932019335

1932119336
function getLongestCandidateIndex(candidates: Signature[], argsCount: number): number {

tests/cases/fourslash/quickInfoCanBeTruncated.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,7 @@ verify.quickInfoIs(`type Less = "_1" | "_2" | "_3" | "_4" | "_5" | "_6" | "_7" |
519519
goTo.marker("3");
520520
verify.signatureHelp({
521521
marker: "3",
522-
text: `f(s: "_0" | "_1" | "_2" | "_3" | "_4" | "_5" | "_6" | "_7" | "_8" | "_9" | "_10" | "_11" | "_12" | "_13" | "_14" | "_15" | "_16" | "_17" | "_18" | "_19" | "_20" | "_21" | "_22" | "_23" | "_24" | ... 474 more ... | "_499", x: never, y: string): void`
522+
text: `f(s: "_499", x: "_0" | "_1" | "_2" | "_3" | "_4" | "_5" | "_6" | "_7" | "_8" | "_9" | "_10" | "_11" | "_12" | "_13" | "_14" | "_15" | "_16" | "_17" | "_18" | "_19" | "_20" | "_21" | "_22" | "_23" | "_24" | ... 473 more ... | "_498", y: string): void`
523523
});
524524
goTo.marker("4");
525525
verify.quickInfoIs(`type Decomposed = {

tests/cases/fourslash/signatureHelpExplicitTypeArguments.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
verify.signatureHelp(
1010
{ marker: "1", text: "f(x: number, y: string): number" },
11-
{ marker: "2", text: "f(x: {}, y: {}): {}" },
11+
{ marker: "2", text: "f(x: boolean, y: string): boolean" },
1212
// too few -- fill in rest with {}
1313
{ marker: "3", text: "f(x: number, y: {}): number" },
1414
// too many -- ignore extra type arguments
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/// <reference path='fourslash.ts' />
2+
3+
////declare function f<T extends string>(a: T, b: T, c: T): void;
4+
////f("x", /**/);
5+
6+
verify.signatureHelp({
7+
marker: "",
8+
text: 'f(a: "x", b: "x", c: "x"): void',
9+
parameterCount: 3,
10+
parameterName: "b",
11+
parameterSpan: 'b: "x"',
12+
});

tests/cases/fourslash/trailingCommaSignatureHelp.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,6 @@ verify.signatureHelp(
2525
},
2626
{
2727
marker: "b",
28-
text: "f(a: {}): {}",
28+
text: "f(a: 2): 2",
2929
},
3030
);

0 commit comments

Comments
 (0)