Codementor Events

The power of jsDoc

Published Jun 15, 2018Last updated Dec 12, 2018
The power of jsDoc

A lot of developers complains about the weak-typing of Javascript (for good reasons). That's why we've seen the rise of Typescript.
But as neat as it is, Typescript come with some caveats.

  • Hard typing
  • Your code is parsed and changed
  • Extra step to build
  • New syntax to learn

Most of the time, these are easy to deal with or just ignore. Where jsDoc is really good, is that it reduce the pain of weak-type without any drawbacks and even add to the table.

What it is

First, let's see a complex example that we're going to reconstruct along this tutorial.

/**
 * Nice little equine animal
 * @class
 * @extends Animal
 */
class Pony extends Animal {
    /**
     * Pony constructor
     * @param {String} name - This pony's name
     * @param {String} [color=Pony.colors.white] - This pony's color
     */
    constructor (name, color = Pony.colors.white) {
        super(name);
        this.color = color;
        /**
         * This pony's age
         * @type {Number}
         */
        this.age = 0;
    }

    /**
     * Make it run
     * @param {...String} through - Any number of field to run through
     * @example instance.run("field", "land");
     * @return {Pony} Itself
     */
    run (...through) {
        console.log("Run through ", ...through);
        return this;
    }

    /**
     * @typedef {Object} PonyColors
     * @prop {String} white - Pure light color
     * @prop {String} black - Dark color
     * @prop {String} foxy - Reddish color
     */
    /**
     * Get possible pony's colors
     * @static
     * @return {PonyColors}
     */
    static get colors () {
        return {
            white: "#fff",
            black: "#333",
            foxy: "#c57432",
        };
    }
}

And, here's a demo of the advantages (using webstorm). Look closely at the autocomplete tooltips.

animated demo gif

Now, let's go through it step by step.

Description

The easiest use of jsDoc is to describe a function or a class.

/**
 * Nice little equine animal
 */
class Pony extends Animal {
    /**
     * Pony constructor
     */
    constructor (name, color = Pony.colors.white) {
        // ...
    }

    /**
     * Make it run
     */
    run (...through) {
        // ...
    }

    /**
     * Get possible pony's colors
     */
    static get colors () {
        // ...
    }
}

Now, anyone using the code you wrote will have some information on the purpose of each functions or classes.
Depending on your IDE or editor, it will be shown in a tooltip whenever autocomplete kick-off.

Parameters

In the introduction, I talked about the variables types in JS. Here's where we'll tackle the problem.
JsDoc allow you to specify what parameters with what type (or types) are expect by a function. Then, your development environment should warn you if you give a parameter with incompatible type.

/**
 * Pony constructor
 * @param {String} name - A string
 * @param {String|Number} color - A string or a number
 */
constructor (name, color = Pony.colors.white) {
    // ...
}

In the same time, you can give a quick description of the variable use.
If a parameter is optional, just surround it with brackets and specify the default value if relevant.

/**
 * Pony constructor
 * @param {String} name - This pony's name
 * @param {String} [color=Pony.colors.white] - This pony's color
 */
constructor (name, color = Pony.colors.white) {
    // ...
}

We can see the effect, once again, on the autocomplete.

params type demo

Type/callback definition

Sometimes, you could need to define custom types to describe some data you don't want to wrap in a class.

/**
 * @typedef {Object} PonyColors
 * @prop {String} white - Pure light color
 * @prop {String} black - Dark color
 * @prop {String} foxy - Reddish color
 */

That way, you can attach a type and a description to each item of an object.

type definition demo

The same is true for an expected parameter, consider the following:

/**
 * @typedef {Object} CustomData
 * @prop {String} name - Cute name for you pony
 * @prop {String} [color=Pony.colors.white] - Color of its fur
 * @prop {Number} [age=0] - Years since its birth
 */
/**
 * Create a pony
 * @param {CustomData} data
 * @return Pony
 */
function ponyFactory (data) {
    // ...
}

The CustomData type is explained in a @typedef block. Type definition can event extends others with the @extends tag. This is really cool 8)

If a function expect a callback (typical array traverse for example), you can show what parameters will be passed to this callback.

/**
 * @callback PonyCallback
 * @param {Pony} pony - A pony
 * @param {Number} index - Index of the pony
 * @param {Array<Pony>} list - List of all the ponies
 */
/**
 * Execute a function on each created pony
 * @param {PonyCallback} callback - Function called on each pony
 */
function herd (callback) {
    // ...
}

Further and beyond

The jsDoc documentation has a lot of tags for you to use. Each one allowing you to better inform the user of your code.

Honorable mention to @return (define the returned type), @abstract (this class should not be instantiate), @type (specify a type for any variable), @example (show an example of use) ...

Remember, most of the time, you will be the one to maintain your own code. So really, you're doing a service to yourself by documenting the code you write.

And last but not least, there are numerous ways to parse all the documentation and output fully formatted markdown to document your API for example.

Discover and read more posts from Guillaume Martigny
get started