JSDocs primer


This is the core of an email I sent around internally regarding jsdocs. When I was finished I figured, let's share this knowledge :) These are my (current! ;) views on JSDocs and how I use them. Doesn't mean you have to too, but if anything you could learn something here...

First of all, any function should have a jsdoc. This includes private or trivial functions. The description is optional for functions where this is trivial (maybe even discouraged) but mandatory for any other. In case of doubt, try to think what a person might think when this is the first time it would see this function, completely out of context. Note also that tests do not replace the need for jsdocs (in my strong opinion; people don't check out archaic tests just to figure out what a function does).

JSDocs start with /** and are indented by one space on subsequent lines. Your IDE should do this for you. Often IDE's will also fill in some fingerprinting if you press enter after the second star if it precedes an existing function (but not all IDE's will do this completely or correctly!). So if we have:

function foo(does, stuff){ ... return 15; }

And we start a jsdoc:

function foo(does, stuff){ ... return 15; }

Pressing enter after the second * will cause your IDE to fill it up to something like

* @param does
* @param stuff
* @return {Number}
function foo(does, stuff){ ... return 15; }

Okay, great. Webstorm currently has one bug (I already filed a ticket and it should be fixed in the next release), Number should be number.

The agreement for writing types in jsdocs are pretty simple: any primitive type is all lower cased. So boolean, undefined, null, number, string. Anything else (-> any object) starts with an upper case and should be the name of the class it _inherits_ from. Note that any function inherits from Function, any array inherits from Array, and any constructor inherits from Function, then Object (although you won't often annotate a construct as value :).

So above, it should be @return {number} ..., unless of course you actually return a boxed Number, but when do you ever do that :) For jsdocs, we dont deal with "classic" number types like int, uint, float, double, etc. Just number will suffice. If the sub-classification is important then add that in the comment.

If a parameter/property/var can have multiple types, you type a pipe (|) between them. So:

@param {string|number|RegExp} confusion

If a something is an array, you can annotate the type with Array, but often it's more helpful to use type[] if the array is of a single type. This can lead to complications, so if that's the case then just leave it as Array and describe the contents in the comment instead. Some examples:

@var {string[]} foo An array with strings
@var {Function|Function[]} bar A function or an array of functions
@var {number[][]} grid A two dimensional array of numbers

Sometimes it's hard to figure out what to type for a type. In all cases, try to use common sense. If a type exists, obviously use it. But sometimes, for instance with DOM elements, it makes more sense to either generalize (I usually use DOM as the type for any dom element), clarify ({Div|Span}), or be correct anyways ({DivDomElementWhateverJavaStuff}). In Uxebu's case, I often struggle with {bonsai.Rect} vs {Rect}. Oh right, if your app puts some constructors in a namespace/object just use the full qualified name, unless it makes more sense to leave it out:

@var {project.Foo} foo
@var {bonsai.Rect} rect
@var {Rect} rect

Next, there are a few directives you need to know about and use properly. The most used is @param. This tells jsdoc that you're explaining something about a param of the next function in the source code. Please put these parameters in order in the jsdoc, otherwise it's very confusing.

The syntax for @param (and @property and @var) is

* @param {types} paramName
* @var {types} paramName
* @param/@property {types} paramName.foo

If the parameter is optional, wrap it in square brackets (in that case no need to add undefined to the types)

* <@param/@property> {} [name]

If it gets a default value, show that like this:

* <@param/@property> {number} [name=5]

It doesn't really matter how the default value is assigned in your code, btw.

You probably should never use null unless you explicitly pass it on. In mose cases null means "the uninitialized object value" (hence typeof null === 'object'). So if you're expecting an object, but it can sometimes be null, you don't need to explicitly annotate it as such. However, if null represents an important value for you, you probably should annotate that.

I mentioned two more directives; @property and @var.

@var is not used very often, but can be used to formally give a variable a type, in case you need to. It's fine to put the whole thing on one line (including open/close of comment):

/** @var {number} counter This is actually a doomsday device */
var counter;

You don't need to put @vars all over the place though, so please don't.

@property is sometimes found in function jsdocs, to clarify an options parameter or some expected properties on one of the parameters. To name the property, simply put down the var name, dot, and property name. You can chain these to multiple properties (but you won't see this very often).

* @param {Object} options
* @property {number} options.startFrame
* @property {string} options.title
* @property {Object} options.hash
* @property {boolean} options.hash.hasKeys See, chained, ugly!
function foo(options){}

Note that we also use @property for prototype (/instance) properties! Only for non-methods though.

Foo.prototype = {
* @property {number} foo
foo: -1,
/** @property {string} bar Single line is fine too! ;) */
bar: '',
* @param {number} bar
foo: function(bar){

We also have a directive for the return value: @return. Sometimes you'll also see @returns, and to this day I'm not sure (because @returns feels more natural), but I've stuck to @return. The directive is only followed by a type and optional description. You can omit it if the function doesn't explicitly return, or only returns undefined.

@ return {number|string}

That's the most important part of jsdocs for me. Some other interesting @directives I often use as well:

@constructor: obviously used for any constructor. I always put these at the top of the directives. It implies that the function returns an instance of itself and should not be used as a regular function.
@see: in case the target of this jsdoc relates to something else (another function/method, or maybe an url)
@name: not really useful (the coder can usually easily distill this information from the source) unless you actually run a jsdoc generator and it's confused about the name

There are waaaay more directives btw. Check out wikipedia for more details. Stuff like @depricated, @private, and @throws also comes in handy sometimes.

One more thing. If I put any comment above a function jsdoc block, I tend to start on the second line (so after the double star opening) and leave one empty line between the comment and the first @directive. If there aren't any comment, the first @directive starts on the second line (so again, after the double star opening).

JSDocs are an important tool for your JS. They allow external coders to go through your code with much more ease, they allow yourself to understand your spaghetti six months after you wrote it, you can use an automatic jsdoc generator to generate documentation for you, and you can use a variety of tooling to do automated testing and/or reports. In my opinion, they are priceless in the typeless environment we work in, while still not forcing us to a strong typed environment.

Hope I didn't miss anything, probably did. But above all I hope it helps you. Thanks for listening.