Repository: TypeScript
Issue: TypeScript / JavaScript - IntelliSense - Improve JSDoc display for overloaded functions, including ones overloaded with `@overload`
Labels: Suggestion,Help Wanted,Experience Enhancement

<!-- ⚠️⚠️ Do Not Delete This! feature_request_template ⚠️⚠️ -->
<!-- Please read our Rules of Conduct: https://opensource.microsoft.com/codeofconduct/ -->
<!-- Please search existing issues to avoid creating duplicates. -->

<!-- Describe the feature you'd like. -->

- Currently, JSDoc is only correctly displayed when hovering over an overloaded function definition if the function is a class method and overloaded using TypeScript declarations. In such cases, the description from the JSDoc of the function definition is shown when hovering over the function overloads if no own overload description is provided, which is pretty awesome because one does not have to copy the description to the docs of each overload if it is the same for all of them. I would definitely want it to work this way for all overloaded functions,
- However, if the function is defined and the overloads declared with the `function` keyword, no description is borrowed from the definition when displaying JSDoc information for the overloads. And when hovering over the function definition, the JSDoc of the first overload is displayed.
- And if the function is overloaded with the TypeScript-flavored JSDoc [`@overload`](https://devblogs.microsoft.com/typescript/announcing-typescript-5-0/#overload-support-in-jsdoc) tag, no JSDoc information is displayed whatsoever despite the overloads clearly being recognized (function signature and property suggestions use the infromation from overloads' docs).
- Also, when linking to a symbol representing an overloaded function with `@link`, the link displayed ends up leading to the first function declaration instead of its definition, which would be way more appropriate. For example, one might want to store the function in a new variable and use `@see` with `@link` over that variable's declaration to make it possible to find the function code and documentation by following the link. It does not make sense that the user ends up seeing one of the ovarloads just because it happens to be the first one.
- I also occasionally see errors when linking to type properties / methods. I don't dare to say what is more sudden, how they appear or how they vanish. They are usually gone after typing something random and then pressing Ctrl+Z. I managed to capture one of them:
    ![image](https://github.com/microsoft/vscode/assets/36817090/57aeadc0-4898-4ef7-b8bd-f5f7bf6470d0)
    I also notice now there was an error in `@implements`, but it is gone now and I cannot reproduce it :(

## Summary of feature requests
- Always borrow description from function definition when displaying JSDoc for overload if no overload description is providd
- Always display JSDoc information when hovering over function definition, including definitions of functions overloaded with `@overload`
- Make `@link`s lead to function definition instead of first declaration
- Look for and fix errors in handling of `@link` / `@implements` / possibly other JSDoc tags used with imported types

## Example code
Download code at [ts-intellisense-jsdoc-overload](https://github.com/aweebit/ts-intellisense-jsdoc-overload).

### function.ts
```ts
/**
 * @see {@link firstOrRest}
 * @param {T} value
 * @returns {[T]} value packed in array
 */
export function firstOrRest<T>(first: T): [T];
/**
 * @see {@link firstOrRest}
 * @param {*} first
 * @param {...T} rest
 * @returns {T[]} array of parameters after the first
 */
export function firstOrRest<T>(first: any, ...rest: T[]): T[];

/**
 * Returns array with first parameter if it is the only one provided,
 * or otherwise with all remaining parameters.
 * @param {*} first First parameter
 * @param {...*} rest Array of remaining parameters 
 * @returns {Array}
 */
export function firstOrRest(first: any, ...rest: any[]): any[] {
    return arguments.length > 1 ? rest : [first];
}

firstOrRest('first');
firstOrRest('first', 'second');

/**
 * @see {@link firstOrRest}
 */
const firstOrRestFunction = firstOrRest;
```

### function.js
```js
// @ts-check

/**
 * @see {@link firstOrRest}
 * @template T
 * @overload
 * @param {T} value
 * @returns {[T]} value packed in array
 */
/**
 * @see {@link firstOrRest}
 * @template T
 * @overload
 * @param {*} first
 * @param {...T} rest
 * @returns {T[]} array of parameters after the first
 */
/**
 * Returns array with first parameter if it is the only one provided,
 * or otherwise with all remaining parameters.
 * @param {*} first First parameter
 * @param {...*} rest Array of remaining parameters 
 * @returns {Array}
 */
export function firstOrRest(first, ...rest) {
    return arguments.length > 1 ? rest : [first];
}

firstOrRest('first');
firstOrRest('first', 'second');

/**
 * @see {@link firstOrRest}
 */
const firstOrRestFunction = firstOrRest;
```

### index.d.ts
```ts
export interface Interface {
    /**
     * Returns array with first parameter if it is the only one provided,
     * or otherwise with all remaining parameters.
     * @param {*} first First parameter
     * @param {...*} rest Array of remaining parameters 
     * @returns {Array}
     */
    firstOrRest(first: any, ...rest: any[]): any[];
}
```

### method.ts
```ts
import type { Interface } from '.';

export class Implementation implements Interface {
    /**
     * @see {@link Interface.firstOrRest}
     * @param {T} value
     * @returns {[T]} value packed in array
     */
    firstOrRest<T>(value: T): [T];
    /**
     * @see {@link Interface.firstOrRest}
     * @param {*} first
     * @param {...T} rest
     * @returns {T[]} array of parameters after the first
     */
    firstOrRest<T>(first: any, ...rest: T[]): T[];

    /**
     * @see {@link Interface.firstOrRest}
     */
    firstOrRest(first: any, ...rest: any[]): any[] {
        return arguments.length > 1 ? rest : [first];
    }
}

const { firstOrRest } = Implementation.prototype;

firstOrRest('first');
firstOrRest('first', 'second');

/**
 * @see {@link Implementation.firstOrRest}
 */
const firstOrRestFunction = firstOrRest;
```

### method.js
```js
// @ts-check

/**
 * @typedef {import ('.').Interface} Interface
 * @implements {Interface}
 */
export class Implementation {
    /**
     * @see {@link Interface.firstOrRest}
     * @template T
     * @overload
     * @param {T} value
     * @returns {[T]} value packed in array
     */
    /**
     * @see {@link Interface.firstOrRest}
     * @template T
     * @overload
     * @param {*} first
     * @param {...T} rest
     * @returns {T[]} array of parameters after the first
     */
    /**
     * @see {@link Interface.firstOrRest}
     * @param {*} first First parameter
     * @param {Array} rest Array of remaining parameters 
     * @returns {Array}
     */
    firstOrRest(first, ...rest) {
        return arguments.length > 1 ? rest : [first];
    }
}

const { firstOrRest } = Implementation.prototype;

firstOrRest('first');
firstOrRest('first', 'second');

/**
 * @see {@link Implementation.firstOrRest}
 */
const firstOrRestFunction = firstOrRest;
```