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
@var
s 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.