21

I'm writing JavaScript (ES6) code in Visual Studio Code and enabled VSCode's type checking as explained in the VSCode docs.

When referring to a type that is defined in another file (Track in the example below), I get an error like [js] Cannot find name 'Track' at the JSDoc reference to that type unless I import it. When I import this type, I get an error from ESLint: [eslint] 'Track' is defined but never used. (no-unused-vars)

I don't want to disable the ESLint rule. Is there a way to import the type only for the type checks in VSCode?

import Track from "./Track";

export default class TrackList {

/**
 * Creates a new track list.
 * @param {Iterable<Track>} tracks the tracks to include
 */
constructor(tracks) {
    this._tracks = tracks ? Array.from(tracks) : [];
}
...
Mike
  • 14,010
  • 29
  • 101
  • 161
ralfstx
  • 3,893
  • 2
  • 25
  • 41
  • Have you tried to add `Track` to the [globals](https://eslint.org/docs/user-guide/configuring#specifying-globals) in ESLint config? – Alex Dec 26 '17 at 11:10
  • Or [disable rules with comments](https://eslint.org/docs/user-guide/configuring#disabling-rules-with-inline-comments) in ESLint – Alex Dec 26 '17 at 11:15
  • @Alex Adding `Track` to globals does not help. JSDoc needs a reference to the source code. Disabling rules would help, but I don't like to disable the rule entirely as this would limit the use of ESLint. Disabling it only for certain names works via the [varsignorepattern](https://eslint.org/docs/rules/no-unused-vars#varsignorepattern) option but using this pattern in every other file would be rather ugly. – ralfstx Dec 26 '17 at 13:25
  • @ralfstx did you try Alex's link to the comment syntax to disable rules for a specific line or section? I had trouble getting the single-line versions to work but I was able to use a disable comment, then import, then an enable comment, and I get the best of both worlds -- eslint checks everything else, and Intellisense uses the correct type information. – Coderer May 10 '18 at 08:44

4 Answers4

17

I have a similar problem and here is how I solved it.

//file.d.ts
export interface Foo {
    bar: number;
}

export as namespace Baz;

Doing this will make the Baz namespace global so you can use it anywhere without importing

/**
 * @param {Baz.Foo} arg 
 */
function test(arg) {

}

Now you get intellisense and type checking in VSCode.
VS Code

Jeremy
  • 776
  • 1
  • 7
  • 18
  • I find this answer fascinating, thank you for that. This way we can keep types in a single d.ts file and "import" them across the codebase within JSDoc without actually having to write "import" everywhere. What's not to like? – Vasek Tobey Vlcek Jan 15 '22 at 11:08
6

Just to keep this topic updated:

With typescript@2.9 you will be able to import JSDoc typedefs, that are automatically exported.

Take a look at this issue with a real working example. Also you can walk along webpack's code to see how they used JSDoc and typescript ti statically chech their pure JS sourcecode base. here is their issue with JSDoc conventions where you can get insipration.

Sergio
  • 61
  • 1
  • 2
  • 2
    To make it easier, applying this answer to your case it would mean adding `/** @typedef { import('./Track').default } Track */` – Tiago A. Mar 12 '21 at 17:22
5

You can use import("%YOUR_LIB%").%TYPE%:

export default class TrackList {

/**
 * Creates a new track list.
 * @param {Iterable<import("./Track").Track>} tracks the tracks to include
 */
constructor(tracks) {
    this._tracks = tracks ? Array.from(tracks) : [];
}
...
Mike
  • 14,010
  • 29
  • 101
  • 161
  • 1
    Perfect... and hideous. `;^)` How in the world did you come across that? – ruffin Jul 24 '23 at 20:05
  • 1
    I've got this hint from some IDE dev. team, when I've opened a ticket that code completion works not as expected. – Mike Jul 25 '23 at 07:17
1

The workaround to disable es-lint warning: using //eslint-disable-line no-unused-vars comment in the end of line with unused import.

import Track from './Track'; //eslint-disable-line no-unused-vars

export default class TrackList {

/**
 * Creates a new track list.
 * @param {Iterable<Track>} tracks the tracks to include
 */
constructor(tracks) {
  this._tracks = tracks ? Array.from(tracks) : [];
}
...
Alex Elkin
  • 574
  • 6
  • 11