diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index aa0091f99523a..010060ba6f184 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9765,23 +9765,24 @@ namespace ts { function getExpandedParameters(sig: Signature, skipUnionExpanding?: boolean): readonly (readonly Symbol[])[] { if (signatureHasRestParameter(sig)) { const restIndex = sig.parameters.length - 1; - const restType = getTypeOfSymbol(sig.parameters[restIndex]); + const restParameter = sig.parameters[restIndex]; + const restType = getTypeOfSymbol(restParameter); if (isTupleType(restType)) { - return [expandSignatureParametersWithTupleMembers(restType, restIndex)]; + return [expandSignatureParametersWithTupleMembers(restType, restIndex, restParameter.escapedName)]; } else if (!skipUnionExpanding && restType.flags & TypeFlags.Union && every((restType as UnionType).types, isTupleType)) { - return map((restType as UnionType).types, t => expandSignatureParametersWithTupleMembers(t as TupleTypeReference, restIndex)); + return map((restType as UnionType).types, t => expandSignatureParametersWithTupleMembers(t as TupleTypeReference, restIndex, restParameter.escapedName)); } } return [sig.parameters]; - function expandSignatureParametersWithTupleMembers(restType: TupleTypeReference, restIndex: number) { + function expandSignatureParametersWithTupleMembers(restType: TupleTypeReference, restIndex: number, defaultName: __String) { const elementTypes = getTypeArguments(restType); const associatedNames = restType.target.labeledElementDeclarations; const restParams = map(elementTypes, (t, i) => { // Lookup the label from the individual tuple passed in before falling back to the signature `rest` parameter name const tupleLabelName = !!associatedNames && getTupleElementLabel(associatedNames[i]); - const name = tupleLabelName || getParameterNameAtPosition(sig, restIndex + i); + const name = tupleLabelName || getParameterNameAtPosition(restType, i, defaultName); const flags = restType.target.elementFlags[i]; const checkFlags = flags & ElementFlags.Variable ? CheckFlags.RestParameter : flags & ElementFlags.Optional ? CheckFlags.OptionalParameter : 0; @@ -9791,6 +9792,15 @@ namespace ts { }); return concatenate(sig.parameters.slice(0, restIndex), restParams); } + + // overwrite function, for it could not handle rest type is union condition + function getParameterNameAtPosition(restType: Type, posInRest: number, defaultName: __String): __String { + if (isTupleType(restType)) { + const associatedNames = ((restType).target).labeledElementDeclarations; + return associatedNames && getTupleElementLabel(associatedNames[posInRest]) || defaultName + "_" + posInRest as __String; + } + return defaultName; + } } function getDefaultConstructSignatures(classType: InterfaceType): Signature[] { diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index 54447417a2a49..9687e31b48512 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -31,14 +31,14 @@ namespace ts.SignatureHelp { } // Only need to be careful if the user typed a character and signature help wasn't showing. - const onlyUseSyntacticOwners = !!triggerReason && triggerReason.kind === "characterTyped"; + const onlyUseSyntacticOwners = triggerReason?.kind === "characterTyped"; // Bail out quickly in the middle of a string or comment, don't provide signature help unless the user explicitly requested it. if (onlyUseSyntacticOwners && (isInString(sourceFile, position, startingToken) || isInComment(sourceFile, position))) { return undefined; } - const isManuallyInvoked = !!triggerReason && triggerReason.kind === "invoked"; + const isManuallyInvoked = triggerReason?.kind === "invoked"; const argumentInfo = getContainingArgumentInfo(startingToken, position, sourceFile, typeChecker, isManuallyInvoked); if (!argumentInfo) return undefined; @@ -125,8 +125,8 @@ namespace ts.SignatureHelp { return name === undefined ? undefined : firstDefined(program.getSourceFiles(), sourceFile => firstDefined(sourceFile.getNamedDeclarations().get(name), declaration => { const type = declaration.symbol && typeChecker.getTypeOfSymbolAtLocation(declaration.symbol, declaration); - const callSignatures = type && type.getCallSignatures(); - if (callSignatures && callSignatures.length) { + const callSignatures = type?.getCallSignatures(); + if (callSignatures?.length) { return typeChecker.runWithCancellationToken(cancellationToken, typeChecker => createSignatureHelpItems(callSignatures, callSignatures[0], argumentInfo, sourceFile, typeChecker)); } })); @@ -217,7 +217,7 @@ namespace ts.SignatureHelp { const info = getArgumentOrParameterListInfo(node, sourceFile); if (!info) return undefined; const { list, argumentIndex, argumentCount, argumentsSpan } = info; - const isTypeParameterList = !!parent.typeArguments && parent.typeArguments.pos === list.pos; + const isTypeParameterList = parent.typeArguments?.pos === list.pos; return { isTypeParameterList, invocation: { kind: InvocationKind.Call, node: invocation }, argumentsSpan, argumentIndex, argumentCount }; } else if (isNoSubstitutionTemplateLiteral(node) && isTaggedTemplateExpression(parent)) { @@ -605,7 +605,7 @@ namespace ts.SignatureHelp { const isVariadic = checker.hasEffectiveRestParameter(candidateSignature); const printer = createPrinter({ removeComments: true }); const typeParameterParts = mapToDisplayParts(writer => { - if (candidateSignature.typeParameters && candidateSignature.typeParameters.length) { + if (candidateSignature.typeParameters?.length) { const args = factory.createNodeArray(candidateSignature.typeParameters.map(p => checker.typeParameterToDeclaration(p, enclosingDeclaration, signatureHelpNodeBuilderFlags)!)); printer.writeList(ListFormat.TypeParameters, args, sourceFile, writer); } diff --git a/tests/cases/fourslash/signatureHelpRestTuplesUnion.ts b/tests/cases/fourslash/signatureHelpRestTuplesUnion.ts new file mode 100644 index 0000000000000..687d53d0810e3 --- /dev/null +++ b/tests/cases/fourslash/signatureHelpRestTuplesUnion.ts @@ -0,0 +1,19 @@ +/// + + +//// export function foo(...args: [string, string] | [number, string, string] +//// ) { +//// } +//// foo(/*1*/) + +verify.signatureHelp( + { + marker: "1", + text: "foo(args_0: string, args_1: string): void", + overloadsCount: 2, + parameterCount: 2, + parameterName: "args_0", + parameterSpan: "args_0: string", + isVariadic: false, + }, +);