Skip to content

Commit 6648009

Browse files
Handle literal property names and skip rest in destructured-param signature help
Follow-up to review feedback: the destructured-parameter doc helper rejected string/numeric property names (`{ "foo": x }`) because of an identifier-only guard, and it could borrow the documentation of a same-named property for an object-rest binding (`{ a, ...rest }`). Use isPropertyNameLiteral so quoted and numeric names resolve while computed/private names are skipped, and skip rest elements via dotDotDotToken. Extends the fourslash test accordingly. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 62911ab commit 6648009

2 files changed

Lines changed: 43 additions & 1 deletion

File tree

src/services/signatureHelp.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ import {
5050
isOmittedExpression,
5151
isParameter,
5252
isPropertyAccessExpression,
53+
isPropertyNameLiteral,
5354
isSourceFile,
5455
isSourceFileJS,
5556
isSpreadElement,
@@ -823,8 +824,13 @@ function getDestructuredParameterDocumentation(parameter: Symbol, checker: TypeC
823824
const parts: SymbolDisplayPart[] = [];
824825
for (const element of declaration.name.elements) {
825826
if (isOmittedExpression(element)) continue;
827+
// A rest element (`...rest`) captures the remaining properties; its name is not a property
828+
// of the object type, so never attempt to resolve documentation for it.
829+
if (element.dotDotDotToken) continue;
826830
const nameNode = element.propertyName || element.name;
827-
if (!isIdentifier(nameNode)) continue;
831+
// Property names may be identifiers, string literals or numeric literals; computed and
832+
// private names cannot be resolved statically and are skipped.
833+
if (!isPropertyNameLiteral(nameNode)) continue;
828834
const propertyName = getTextOfIdentifierOrLiteral(nameNode);
829835
const propertyDocumentation = firstDefined(types, type => {
830836
const property = type.getProperty(propertyName);

tests/cases/fourslash/signatureHelpJSDocDestructuredParams.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,21 @@
1919
//// */
2020
////function withoutParentDoc({ id, label }) {}
2121
////withoutParentDoc(/*2*/);
22+
////
23+
/////**
24+
//// * @param {Object} opts
25+
//// * @param {string} opts.foo a foo
26+
//// */
27+
////function quotedName({ "foo": x }) {}
28+
////quotedName(/*3*/);
29+
////
30+
/////**
31+
//// * @param {Object} opts
32+
//// * @param {number} opts.a aaa
33+
//// * @param {number} opts.rest REST_DOC
34+
//// */
35+
////function withRest({ a, ...rest }) {}
36+
////withRest(/*4*/);
2237

2338
verify.signatureHelp(
2439
{
@@ -41,4 +56,25 @@ verify.signatureHelp(
4156
{ name: "param", text: [{ text: "opts.label", kind: "parameterName" }, { text: " ", kind: "space" }, { text: "The display label.", kind: "text" }] },
4257
],
4358
},
59+
{
60+
// Quoted (string-literal) property name resolves its nested @param doc.
61+
marker: "3",
62+
parameterName: "__0",
63+
parameterDocComment: "foo: a foo",
64+
tags: [
65+
{ name: "param", text: [{ text: "opts", kind: "text" }] },
66+
{ name: "param", text: [{ text: "opts.foo", kind: "parameterName" }, { text: " ", kind: "space" }, { text: "a foo", kind: "text" }] },
67+
],
68+
},
69+
{
70+
// Object-rest binding must not borrow the doc of a same-named property.
71+
marker: "4",
72+
parameterName: "__0",
73+
parameterDocComment: "a: aaa",
74+
tags: [
75+
{ name: "param", text: [{ text: "opts", kind: "text" }] },
76+
{ name: "param", text: [{ text: "opts.a", kind: "parameterName" }, { text: " ", kind: "space" }, { text: "aaa", kind: "text" }] },
77+
{ name: "param", text: [{ text: "opts.rest", kind: "parameterName" }, { text: " ", kind: "space" }, { text: "REST_DOC", kind: "text" }] },
78+
],
79+
},
4480
);

0 commit comments

Comments
 (0)