diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 958ceae039..43ca06fa21 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -112,6 +112,7 @@ namespace ts { getMergedSymbol, getDiagnostics, getGlobalDiagnostics, + getExpandedParameters, getTypeOfSymbolAtLocation: (symbol, location) => { location = getParseTreeNode(location); return location ? getTypeOfSymbolAtLocation(symbol, location) : errorType; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index cce869df44..21389cb2e7 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -3032,6 +3032,7 @@ namespace ts { /* @internal */ getPromisedTypeOfPromise(promise: Type, errorNode?: Node): Type | undefined; getReturnTypeOfSignature(signature: Signature): Type; + /* @internal */ getExpandedParameters(sig: Signature): ReadonlyArray; /** * Gets the type of a parameter at a given position in a signature. * Returns `any` if the index is not valid. diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index 27ed0b6dc2..41bc068c77 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -566,7 +566,7 @@ namespace ts.SignatureHelp { } function itemInfoForParameters(candidateSignature: Signature, checker: TypeChecker, enclosingDeclaration: Node, sourceFile: SourceFile): SignatureHelpItemInfo { - const isVariadic = candidateSignature.hasRestParameter; + let isVariadic = candidateSignature.hasRestParameter; const printer = createPrinter({ removeComments: true }); const typeParameterParts = mapToDisplayParts(writer => { if (candidateSignature.typeParameters && candidateSignature.typeParameters.length) { @@ -574,7 +574,16 @@ namespace ts.SignatureHelp { printer.writeList(ListFormat.TypeParameters, args, sourceFile, writer); } }); - const parameters = candidateSignature.parameters.map(p => createSignatureHelpParameterForParameter(p, checker, enclosingDeclaration, sourceFile, printer)); + + const expandedParameters = checker.getExpandedParameters(candidateSignature); + const parameters = expandedParameters.map(p => createSignatureHelpParameterForParameter(p, checker, enclosingDeclaration, sourceFile, printer)); + + // If parameters are not the same as the expanded parameters, we need to reevaluate whether this a variadic signature based on the last parameter + if (expandedParameters !== candidateSignature.parameters) { + const restCandidate = lastOrUndefined(expandedParameters); + isVariadic = !!restCandidate && !!(getCheckFlags(restCandidate) & CheckFlags.RestParameter); + } + return { isVariadic, parameters, prefix: [...typeParameterParts, punctuationPart(SyntaxKind.OpenParenToken)], suffix: [punctuationPart(SyntaxKind.CloseParenToken)] }; } diff --git a/tests/cases/fourslash/signatureHelpCallExpressionTuples.ts b/tests/cases/fourslash/signatureHelpCallExpressionTuples.ts new file mode 100644 index 0000000000..bcad3ccde4 --- /dev/null +++ b/tests/cases/fourslash/signatureHelpCallExpressionTuples.ts @@ -0,0 +1,56 @@ +/// + + +//// function fnTest(str: string, num: number) { } +//// declare function wrap(fn: (...a: A) => R) : (...a: A) => R; +//// var fnWrapped = wrap(fnTest); +//// fnWrapped/*3*/(/*1*/'', /*2*/5); +//// function fnTestVariadic (str: string, ...num: number[]) { } +//// var fnVariadicWrapped = wrap(fnTestVariadic); +//// fnVariadicWrapped/*4*/(/*5*/'', /*6*/5); +//// function fnNoParams () { } +//// var fnNoParamsWrapped = wrap(fnNoParams); +//// fnNoParamsWrapped/*7*/(/*8*/); + +verify.quickInfoAt("3", "var fnWrapped: (str: string, num: number) => void"); +verify.signatureHelp( + { + marker: "1", + text: "fnWrapped(str: string, num: number): void", + parameterCount: 2, + parameterName: "str", + parameterSpan: "str: string", + }, + { + marker: "2", + parameterName: "num", + parameterSpan: "num: number", + }, +); + +verify.quickInfoAt("4", "var fnVariadicWrapped: (str: string, ...num: number[]) => void"); +verify.signatureHelp( + { + marker: "5", + text: "fnVariadicWrapped(str: string, ...num: number[]): void", + parameterCount: 2, + parameterName: "str", + parameterSpan: "str: string", + isVariadic: true, + }, + { + marker: "6", + parameterName: "num", + parameterSpan: "...num: number[]", + isVariadic: true, + }, +); + +verify.quickInfoAt("7", "var fnNoParamsWrapped: () => void"); +verify.signatureHelp( + { + marker: "8", + text: "fnNoParamsWrapped(): void", + parameterCount: 0, + } +);