Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 3, 2025

Fixed the issue where destructured interface members were not showing inherited JSDoc comments when hovering.

Summary

When hovering over destructured variables from interfaces (e.g., const {foo} = fubar where fubar has an interface type), the hover information was showing type information but omitting JSDoc comments from the source interface properties.

Changes Made

  1. Modified getDocumentationFromDeclaration in internal/ls/hover.go to detect binding elements and retrieve documentation from the corresponding property in the source type
  2. Implemented proper fallback logic: First tries to get JSDoc from the binding element itself, only falls back to the property's JSDoc if none is found (matching TypeScript's behavior)
  3. Added findPropertyInType helper function to handle union types properly (simplified using IsUnion() and Types() methods)
  4. Updated function signature to pass symbol and location for proper context
  5. Updated signature help call site in internal/ls/signaturehelp.go with new signature

Testing

  • Added comprehensive tests for both basic and renamed destructuring patterns:
    • TestDestructuredInterfaceJSDoc: Tests basic destructuring ({foo, bar}) and renamed destructuring ({baz: biz})
    • TestDestructuredInterfaceJSDocWithRename: Tests renamed destructuring with JSDoc inheritance
    • TestDestructuredWithOwnJSDoc: Verifies behavior when binding elements have their own JSDoc (demonstrates current TypeScript behavior where binding element JSDoc is not attached)
  • Updated 4 baseline tests that now correctly show JSDoc comments for destructured properties
  • All existing tests pass
  • No security vulnerabilities detected by CodeQL

Implementation Details

The fix matches TypeScript's reference implementation in symbolDisplay.ts (lines 787-799). The implementation:

  • Checks if documentation was found for the binding element declaration
  • Only if no documentation exists, looks up the property from the interface type
  • For union types, searches through constituent types to find the property
  • Uses ValueDeclaration for binding element detection (per code review feedback)
Original prompt

This section details on the original issue you should resolve

<issue_title>Destructured interface members lack inherited JSDoc comments</issue_title>
<issue_description>### Extension Version

0.20251203.1

VS Code Version

1.106.3

Operating system Version

Ubuntu x86-64, running via Windows 11 inside WSL

Steps to reproduce

  1. Copy this MRE:
    interface FooBar {
        /** foo comment */
        foo: number;
        /** bar comment */
        bar: string;
        /** baz comment */
        baz: string;
    }
    
    declare const fubar: FooBar;
    
    const {foo, bar, baz} = fubar;
  2. hover over properties
  3. no comments
Image

Issue

Should have hovers, as is present in normal typescript</issue_description>

<agent_instructions>Make a fourslash test in ./internal/fourslash/tests first; see internal/fourslash/tests/hoverAliasInImportedFile_test.go for an example.

Remember that you have a reference hover implementation in the submodule to cross compare.

You can use print debugging with go test -v as well.</agent_instructions>

Comments on the Issue (you are @copilot in this section)


✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.

Copilot AI and others added 4 commits December 3, 2025 17:56
Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix issue with inherited JSDoc comments in destructured members Fix missing JSDoc comments on destructured interface properties Dec 3, 2025
Copilot AI requested a review from jakebailey December 3, 2025 18:17
Co-authored-by: jakebailey <5341706+jakebailey@users.noreply.github.com>
Copilot AI requested a review from jakebailey December 3, 2025 19:10
@jakebailey jakebailey marked this pull request as ready for review December 3, 2025 19:12
Copilot AI review requested due to automatic review settings December 3, 2025 19:12
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes an issue where destructured interface members were not showing their inherited JSDoc comments when hovering over them in the editor. The fix adds special handling for binding elements (variables created from destructuring) in the hover implementation to retrieve documentation from the corresponding property in the source type.

Key Changes

  • Modified getDocumentationFromDeclaration to detect binding elements and retrieve documentation from the interface property
  • Added helper function findPropertyInType to handle union types when searching for properties
  • Updated function signature to pass symbol and location context
  • Added comprehensive tests for both basic and renamed destructuring scenarios

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.

File Description
internal/ls/hover.go Added binding element detection logic and helper function to find property documentation in source types, updated function signature
internal/ls/signaturehelp.go Updated call to getDocumentationFromDeclaration with new signature
internal/fourslash/tests/destructuredInterfaceJSDoc_test.go Added new tests for destructured interface JSDoc comments, including renamed properties
testdata/baselines/reference/fourslash/quickInfo/*.baseline Updated 4 baseline tests to correctly show JSDoc comments for destructured properties

Copy link
Member

@jakebailey jakebailey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is not unreasonable; it is the same code, but things are shifted around in the Go version.

Copy link
Member

@DanielRosenwasser DanielRosenwasser left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot address

declare const fubar: FooBar;
const {/*1*/foo, /*2*/bar, /*3*/baz} = fubar;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change the test case to use a renamed destructuring

const {/*1*/foo, /*2*/bar, /*3*/baz: /*4*/biz} = fubar;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in f6b3006. Updated the test to include renamed destructuring: const {foo, bar, baz: biz} = fubar; where biz is tested to ensure it shows the JSDoc comment from baz.

return "", ""
}
return quickInfo, l.getDocumentationFromDeclaration(c, declaration, contentFormat)
return quickInfo, l.getDocumentationFromDeclaration(c, symbol, declaration, node, contentFormat)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not so familiar with this, so it could be right, but what I would say is that maybe this needs to be more general and look at all declarations nodes from a given symbol like getDocumentationComment did in the original codebase.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in f6b3006. Made the implementation more general by iterating through all symbol.Declarations (instead of just checking symbol.ValueDeclaration) to find binding elements. This matches the pattern in TypeScript where getDocumentationComment processes all declarations from a symbol.

…est with renamed destructuring

Co-authored-by: DanielRosenwasser <972891+DanielRosenwasser@users.noreply.github.com>
Comment on lines 25 to 31
const {/*1*/foo, /*2*/bar, baz: /*3*/biz} = fubar;
`
f, done := fourslash.NewFourslash(t, nil /*capabilities*/, content)
defer done()
f.VerifyQuickInfoAt(t, "1", "const foo: number", "foo comment")
f.VerifyQuickInfoAt(t, "2", "const bar: string", "bar comment")
f.VerifyQuickInfoAt(t, "3", "const biz: string", "baz comment")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a marker before baz:

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot do it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 8a13eab. Added marker before baz: to test that hovering over the property name shows (property) FooBar.baz: string with the JSDoc comment, while hovering over biz shows const biz: string with the same JSDoc comment.

Co-authored-by: DanielRosenwasser <972891+DanielRosenwasser@users.noreply.github.com>
Comment on lines 91 to 92
if propertySymbol != nil && propertySymbol.ValueDeclaration != nil {
declaration = propertySymbol.ValueDeclaration
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot I was actually expecting all comments for a given symbol to be smushed together, but that's not what the old codebase does either.

So I think we actually should switch back to the ValueDeclaration approach if we're doing anything like this.

Also, look for, or add, a test for quick info at 1 and 2.

interface Foo {
    /** This is bar from the interface */
    bar: string;
    /** This is baz from the interface */
    baz: number;
}

declare var foo: Foo;

/** Comment on the variable statement. */
const {
    /** Comment on bar destructuring. */ /*1*/bar,
    /** Comment on baz destructuring. */ /*2*/baz
} = foo;

@copilot go.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 73c9497. Switched back to the ValueDeclaration approach and restructured the logic to match TypeScript's behavior: first try to get JSDoc from the binding element itself, only fall back to the property's JSDoc if none is found.

Added test TestDestructuredWithOwnJSDoc that verifies the behavior when binding elements have JSDoc comments before them. The test shows that TypeScript doesn't currently attach JSDoc to individual binding elements in destructuring patterns, so both bar and baz correctly fall back to showing the interface property's JSDoc ("This is bar from the interface" and "This is baz from the interface").

…JSDoc

Co-authored-by: DanielRosenwasser <972891+DanielRosenwasser@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Destructured interface members lack inherited JSDoc comments

3 participants