I’ve been using JSDoc for some time now, and with the recent hype because of Svelte migration, today I come to share tips and how to use them.
Those are the very basics of what you will need.
Typescript
The syntax for some things is different, but for most things, just think of it as Typescript and you’ll be fine.
.d.ts files
I usually use them for helper and utility types (it’s easier to create them, especially those with generics and you can then just use them)
Setup
Add // @ts-check
to the top of each file you need or add a jsconfig,json
in the root of your project with the check JS files. (Don’t forget to enable js checking
in your IDE).
JSDoc
@type
Just as with TS, there's a difference between:
/** @type {Foo} */
const foo = {}
const foo: Foo = {}
// and
const foo = /** @type {Foo} */ ({})
const foo = {} as Foo
// also includes
const foo = /** @satisfies {Foo} */ ({})
Depending on how you want TS to handle the type, autocomplete, and errors that it shows you.
For @type
I usually go with it inline because I normally use it to cast something to the type I need:
// React common example
const [state, setState] = useState(/** @type {{ foo: Bar, bar: Baz }} */ ({}));
// ^? { foo: Bar, bar: Baz }
// And then sometimes you need some casting
Object.keys(state).map(k => k)
// ^? string
/** @type {(keyof state)[]} */ (Object.keys(state)).map(k => k)
// ^? "foo"|"bar"
And, as you could see when casting something to a type you put it between parenthesis /** @type {Foo} */ (toBeCast)
, and of course, if you need you can /** @type {Foo} */(/** @type {unknown} */ (toBeCast))
and yes, you need to put it inside two for this.
@typedef
You’ll probably use a lot, usually for more complex types.
For this one, I declare it as a block and usually in the first manner without any comments.
But sometimes you want the second version because wherever those types go, so do the comments.
/**
* @template {string} [T='bar']
* @typedef {{
* bar: T, // you can't see this comment
* baz?: string,
* }} Foo visible comments here
*/
/**
* @template {string} [T='bar']
* @typedef {Object} Foo2
* @property {T} bar this comment is visible
* @property {string} [baz] this is an optional property
*/
// both equivalent to:
type Foo<T extends string = "bar"> = {
bar: T;
baz?: string;
}
About the @template
, the simplest way to declare a generic is @template T
.
The one in the example is the most complex one you would need with a type it extends and then a default value, but you can also only have only one or the other.
Top comments (2)
I started using jsdoc in a new project and I'm really liking what the annotations do to my IDE.
I suffered enough in a legacy project and I'm happy JSDoc is getting some hype.
JSDoc support is already good, and it will only get better. (I even found a bug, reported and people already fixed!)