JSDoc has been my go-to tool for documenting JavaScript code for a while, and with the recent buzz around Svelte migration, I decided to share some tips and tricks on how to use it.
These are the essential things you need to know.
Typescript
Some things have a different way of writing them, but for most things, you can use TypeScript logic and it will work.
.d.ts files
My common use case for them is to define types for helper and utility functions (they are more convenient to create, especially with generics, and then I can just apply 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.
No comments:
Post a Comment