Skip to content

In pickLongestCandidateSignature, instantiate using inferred type arguments #26646

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
1 commit merged into from
Aug 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 34 additions & 19 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18636,7 +18636,7 @@ namespace ts {
return getInferredTypes(context);
}

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

// The following variables are captured and modified by calls to chooseOverload.
// If overload resolution or type argument inference fails, we want to report the
Expand Down Expand Up @@ -19225,6 +19213,21 @@ namespace ts {
}
}

function getExcludeArgument(args: ReadonlyArray<Expression>): boolean[] | undefined {
let excludeArgument: boolean[] | undefined;
// We do not need to call `getEffectiveArgumentCount` here as it only
// applies when calculating the number of arguments for a decorator.
for (let i = 0; i < args.length; i++) {
if (isContextSensitive(args[i])) {
if (!excludeArgument) {
excludeArgument = new Array(args.length);
}
excludeArgument[i] = true;
}
}
return excludeArgument;
}

// No signature was applicable. We have already reported the errors for the invalid signature.
// If this is a type resolution session, e.g. Language Service, try to get better information than anySignature.
function getCandidateForOverloadFailure(
Expand Down Expand Up @@ -19303,17 +19306,29 @@ namespace ts {
return candidate;
}

const typeArgumentNodes: ReadonlyArray<TypeNode> = callLikeExpressionMayHaveTypeArguments(node) ? node.typeArguments || emptyArray : emptyArray;
const typeArgumentNodes: ReadonlyArray<TypeNode> | undefined = callLikeExpressionMayHaveTypeArguments(node) ? node.typeArguments : undefined;
const instantiated = typeArgumentNodes
? createSignatureInstantiation(candidate, getTypeArgumentsFromNodes(typeArgumentNodes, typeParameters, isInJavaScriptFile(node)))
: inferSignatureInstantiationForOverloadFailure(node, typeParameters, candidate, args);
candidates[bestIndex] = instantiated;
return instantiated;
}

function getTypeArgumentsFromNodes(typeArgumentNodes: ReadonlyArray<TypeNode>, typeParameters: ReadonlyArray<TypeParameter>, isJs: boolean): ReadonlyArray<Type> {
const typeArguments = typeArgumentNodes.map(getTypeOfNode);
while (typeArguments.length > typeParameters.length) {
typeArguments.pop();
}
while (typeArguments.length < typeParameters.length) {
typeArguments.push(getConstraintOfTypeParameter(typeParameters[typeArguments.length]) || getDefaultTypeArgumentType(isInJavaScriptFile(node)));
typeArguments.push(getConstraintOfTypeParameter(typeParameters[typeArguments.length]) || getDefaultTypeArgumentType(isJs));
}
const instantiated = createSignatureInstantiation(candidate, typeArguments);
candidates[bestIndex] = instantiated;
return instantiated;
return typeArguments;
}

function inferSignatureInstantiationForOverloadFailure(node: CallLikeExpression, typeParameters: ReadonlyArray<TypeParameter>, candidate: Signature, args: ReadonlyArray<Expression>): Signature {
const inferenceContext = createInferenceContext(typeParameters, candidate, /*flags*/ isInJavaScriptFile(node) ? InferenceFlags.AnyDefault : InferenceFlags.None);
const typeArgumentTypes = inferTypeArguments(node, candidate, args, getExcludeArgument(args), inferenceContext);
return createSignatureInstantiation(candidate, typeArgumentTypes);
}

function getLongestCandidateIndex(candidates: Signature[], argsCount: number): number {
Expand Down
2 changes: 1 addition & 1 deletion tests/cases/fourslash/quickInfoCanBeTruncated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -519,7 +519,7 @@ verify.quickInfoIs(`type Less = "_1" | "_2" | "_3" | "_4" | "_5" | "_6" | "_7" |
goTo.marker("3");
verify.signatureHelp({
marker: "3",
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`
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`
});
goTo.marker("4");
verify.quickInfoIs(`type Decomposed = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

verify.signatureHelp(
{ marker: "1", text: "f(x: number, y: string): number" },
{ marker: "2", text: "f(x: {}, y: {}): {}" },
{ marker: "2", text: "f(x: boolean, y: string): boolean" },
// too few -- fill in rest with {}
{ marker: "3", text: "f(x: number, y: {}): number" },
// too many -- ignore extra type arguments
Expand Down
12 changes: 12 additions & 0 deletions tests/cases/fourslash/signatureHelpInference.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/// <reference path='fourslash.ts' />

////declare function f<T extends string>(a: T, b: T, c: T): void;
////f("x", /**/);

verify.signatureHelp({
marker: "",
text: 'f(a: "x", b: "x", c: "x"): void',
parameterCount: 3,
parameterName: "b",
parameterSpan: 'b: "x"',
});
2 changes: 1 addition & 1 deletion tests/cases/fourslash/trailingCommaSignatureHelp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ verify.signatureHelp(
},
{
marker: "b",
text: "f(a: {}): {}",
text: "f(a: 2): 2",
},
);