A well-named function still says nothing about the unit of its arguments, about what it hands back when it fails, or about which parameters are optional.
JSDoc puts those answers directly above it, in a comment the editor shows at every call site and that TypeScript knows how to verify.
Definition
JSDoc is a convention for writing JavaScript comments. A block opened with /** and made of tags prefixed by an at sign describes the parameters, the returned value and the types involved. The language ignores it, the tooling does not.
/**
* Computes the gross amount of an invoice.
*
* @param {number} netAmount The amount before tax, in euros.
* @param {number} [rate] The tax rate, 0.2 by default.
* @returns {number} The gross amount, rounded to the cent.
*/
function grossTotal(netAmount, rate = 0.2) {
return Math.round(netAmount * (1 + rate) * 100) / 100;
}
console.log(grossTotal(19)); // 22.8The square brackets around rate mark a Default parameter, and therefore an optional argument. The editor repeats these three lines in its tooltip, and completion now knows the nature of every argument.
The everyday tags
| Tag | What it describes |
|---|---|
| @param | One argument: its type, its name, its role |
| @returns | The value handed back, and under what condition |
| @type | The type of a variable or a constant |
| @typedef | A named type, reusable across the whole file |
| @property | One field of the type declared just above |
| @throws | The Error the function may raise |
Six tags cover very nearly every need. The remaining thirty or so mostly serve documentation generators.
Typing a .js file with no compiler
A // @ts-check comment at the top of a file, or the project-wide checkJs option, and the TypeScript checker treats JavaScript exactly like TypeScript, reading the types out of the comments.
/**
* @typedef {object} Invoice
* @property {number} id
* @property {number} amount
* @property {"draft" | "paid"} status
*/
/** @type {Invoice} */
const invoice = { id: 42, amount: 19, status: "settled" };
// Type '"settled"' is not assignable to type '"draft" | "paid"'.The file stays a runnable .js: no build step, no extra tool to install. It is the shortest road to typing an existing code base.
Frequently asked questions
Does JSDoc replace TypeScript?
On a small project, often yes: the same checks, without a compilation step. On a larger base the syntax turns verbose, and advanced features such as conditional types express poorly inside a comment. The move to real TypeScript files then happens on its own.
Should every function be commented?
No. A short function with an explicit name documents itself, and a comment that merely repeats the parameter name adds nothing. Keep JSDoc for exported functions, for those whose contract is not obvious, and for anything carrying optional parameters.
What happens when the comment lies?
Without @ts-check, absolutely nothing: the comment is free text and no one confronts it with the code. With checking turned on it becomes a constraint like any other, and a false annotation raises an error just as a wrong argument would. That is the whole point of switching it on.