Recipe · Functions

How do I mark a parameter as optional in JSDoc?

  • Node.js (ES modules)
  • @param
  • Optional parameters
  • Default values
  • Checked with TypeScript 5.9

The problem Some of my function's parameters can be left out, and some have default values. How do I write that in JSDoc so callers may skip them and tsc still checks what they pass?

The short answer

Put square brackets around the parameter’s name in its @param tag. If the parameter has a default, write the default in the JavaScript itself:

price.js price.ts TypeScript 5.9.3 · checked 21 October 2026
/**
 * @param {number} cents
 * @param {string} [currency] The ISO code; "USD" when left out.
 * @param {{ showCode?: boolean }} [options]
 * @returns {string}
 */
export function formatPrice(cents, currency = "USD", options = {}) {
  const text = new Intl.NumberFormat("en-US", { style: "currency", currency }).format(cents / 100);
  return options.showCode ? `${text} ${currency}` : text;
}

/**
 * @param {string} name
 * @param {string} [note]
 * @returns {string}
 */
export function labelFor(name, note) {
  return note ? `${name} (${note.trim()})` : name;
}

formatPrice(1999);
formatPrice(1999, "EUR");
formatPrice(1999, "EUR", { showCode: true });
labelFor("Desk lamp");
export function formatPrice(cents: number, currency = "USD", options: { showCode?: boolean } = {}): string {
  const text = new Intl.NumberFormat("en-US", { style: "currency", currency }).format(cents / 100);
  return options.showCode ? `${text} ${currency}` : text;
}

export function labelFor(name: string, note?: string): string {
  return note ? `${name} (${note.trim()})` : name;
}

formatPrice(1999);
formatPrice(1999, "EUR");
formatPrice(1999, "EUR", { showCode: true });
labelFor("Desk lamp");
An optional parameter and two with defaults.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

[currency] and [options] tell tsc that callers may leave those arguments out, so all three calls to formatPrice pass. [note] does the same for labelFor, which has no default.

Why it works

TypeScript reads a bracketed @param name as an optional parameter, the JSDoc spelling of note?: string. Inside the function an optional parameter without a default has the type string | undefined, which is why labelFor checks note before calling note.trim().

A JavaScript default value does more: it makes the parameter optional for callers and gives it a value when the argument is missing. Inside formatPrice, currency is a plain string and options is the object type, because the default fills the gap; no check is needed.

The same thing in TypeScript

Switch the first example to the TypeScript equivalent tab. A default becomes currency = "USD", with the type inferred from the value, and an optional parameter without one is note?: string. The two files describe the same functions to the checker.

What goes wrong

Leave the brackets off a typed parameter that has no default, and every call must pass it:

price.js Error confirmed · TypeScript 5.9.3 · checked 21 October 2026
/**
 * @param {number} cents
 * @param {string} currency
 * @returns {string}
 */
export function formatPrice(cents, currency) {
  return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(cents / 100);
}

formatPrice(1999);error TS2554: Expected 2 arguments, but got 1.
A typed parameter without brackets is required.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

Untyped parameters in a .js file are all treated as optional. Once a @param line gives a parameter a type, it is required unless the name is in brackets or the parameter has a default, so calls that used to pass can fail with TS2554 when you add the JSDoc.

The other mistake is on the inside. An optional parameter can be undefined, so using it as if it were always there is TS18048:

label.js Error confirmed · TypeScript 5.9.3 · checked 21 October 2026
/**
 * @param {string} name
 * @param {string} [note]
 * @returns {string}
 */
export function labelFor(name, note) {
  return `${name} (${note.trim()})`;error TS18048: 'note' is possibly 'undefined'.
}
An optional parameter is possibly undefined inside the function.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

A default written only in the JSDoc does not help. [note="no note"] reads well in a hover, but TypeScript does not apply it, so the function still sees undefined when the argument is missing:

label.js Error confirmed · TypeScript 5.9.3 · checked 21 October 2026
/**
 * @param {string} name
 * @param {string} [note="no note"]
 * @returns {string}
 */
export function labelFor(name, note) {
  return `${name} (${note.trim()})`;error TS18048: 'note' is possibly 'undefined'.
}
A default written only in the JSDoc.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

Gotchas

  • Optional parameters come last. A required parameter after an optional one has to be passed anyway, so put the optional ones at the end, or collect them in one options object.
  • {string=} works too. The Closure Compiler form @param {string=} currency means the same as [currency]; pick one spelling per project.
  • Optional is not nullable. [note] allows undefined but not null. If callers pass null, say so: @param {string | null} [note].

Questions

Do I still need the brackets when the parameter has a default value?

No. In a .js file a parameter with a default value (currency = "USD") is optional for callers whatever its @param line says. The brackets are still worth writing: they tell the next reader, and documentation tools, that the argument can be left out.

Is @param {string=} currency the same as [currency]?

Yes. The trailing = is the Closure Compiler spelling of an optional parameter, and TypeScript reads it the same way as square brackets around the name. Square brackets are the more common form today.

Can I pass undefined for an optional parameter?

Yes. An optional parameter accepts undefined, even with exactOptionalPropertyTypes on (that option is about object properties), and a default value applies when the argument is undefined or missing.

Sources

Last reviewed 21 October 2026.