Recipe · Functions

How do I type a callback parameter in JSDoc?

  • Node.js (ES modules)
  • @callback
  • @param
  • Function types
  • Checked with TypeScript 5.9

The problem My function takes a callback. How do I say what arguments the callback gets and what it must return, so the code passed in is checked too?

The short answer

Name the callback’s type with @callback, listing its parameters and return type like a function’s, and use that name in the @param tag. For a small callback, an inline arrow type does the same job:

orders.js orders.ts TypeScript 5.9.3 TypeScript 5.9.3, run 21 October 2026: No errors. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. No errors. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. No errors. checked 21 October 2026
/**
 * @typedef {object} Order
 * @property {string} id
 * @property {number} total
 */

/**
 * @callback OrderTest
 * @param {Order} order
 * @param {number} index
 * @returns {boolean}
 */

/**
 * @param {Order[]} orders
 * @param {OrderTest} keep
 * @returns {Order[]}
 */
export function pickOrders(orders, keep) {
  return orders.filter(keep);
}

/**
 * @param {Order[]} orders
 * @param {(order: Order) => void} onEach
 */
export function forEachOrder(orders, onEach) {
  for (const order of orders) onEach(order);
}

const orders = [
  { id: "A-1001", total: 40 },
  { id: "A-1002", total: 12 },
];
pickOrders(orders, (order) => order.total > 25);
forEachOrder(orders, (order) => console.log(order.id));
export interface Order {
  id: string;
  total: number;
}

type OrderTest = (order: Order, index: number) => boolean;

export function pickOrders(orders: Order[], keep: OrderTest): Order[] {
  return orders.filter(keep);
}

export function forEachOrder(orders: Order[], onEach: (order: Order) => void): void {
  for (const order of orders) onEach(order);
}

const orders = [
  { id: "A-1001", total: 40 },
  { id: "A-1002", total: 12 },
];
pickOrders(orders, (order) => order.total > 25);
forEachOrder(orders, (order) => console.log(order.id));
A callback type named with @callback.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

The arrow functions passed to pickOrders and forEachOrder need no JSDoc of their own: order is an Order because the parameter’s type says so.

Why it works

@callback OrderTest declares a function type, the same as @typedef {(order: Order, index: number) => boolean} OrderTest, but with room for a description on every line. When a function literal is passed where that type is expected, TypeScript types its parameters from the context. That is why order.total is checked without an annotation, and why the callback’s return value is checked against boolean.

The same thing in TypeScript

The TypeScript equivalent tab declares type OrderTest = (order: Order, index: number) => boolean; and the inline (order: Order) => void as a parameter annotation. A @callback block and a function type alias are interchangeable; tsc treats them the same.

What goes wrong

Because the callback’s parameter is typed from the context, a misspelled property inside it is caught where you write it (TS2551):

orders.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: orders.js(18,33): error TS2551: Property 'totl' does not exist on type 'Order'. Did you mean 'total'? 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. orders.js(18,33): error TS2551: Property 'totl' does not exist on type 'Order'. Did you mean 'total'? 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. orders.js(18,33): error TS2551: Property 'totl' does not exist on type 'Order'. Did you mean 'total'? checked 21 October 2026
/** @typedef {{ id: string, total: number }} Order */

/**
 * @callback OrderTest
 * @param {Order} order
 * @returns {boolean}
 */

/**
 * @param {Order[]} orders
 * @param {OrderTest} keep
 */
export function pickOrders(orders, keep) {
  return orders.filter(keep);
}

pickOrders([], (order) => order.totl > 25);error TS2551: Property 'totl' does not exist on type 'Order'. Did you mean 'total'?
The callback's parameter is typed from the @callback.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

And the return type is part of the contract. A callback that returns the order’s id where a boolean is expected fails with TS2322:

orders.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: orders.js(18,27): error TS2322: Type 'string' is not assignable to type 'boolean'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. orders.js(18,27): error TS2322: Type 'string' is not assignable to type 'boolean'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. orders.js(18,27): error TS2322: Type 'string' is not assignable to type 'boolean'. checked 21 October 2026
/** @typedef {{ id: string, total: number }} Order */

/**
 * @callback OrderTest
 * @param {Order} order
 * @returns {boolean}
 */

/**
 * @param {Order[]} orders
 * @param {OrderTest} keep
 */
export function pickOrders(orders, keep) {
  return orders.filter(keep);
}

pickOrders([], (order) => order.id);error TS2322: Type 'string' is not assignable to type 'boolean'.
A callback that returns the wrong type.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

Gotchas

  • A @callback without @returns returns any. The type stays open, so nothing checks what the callback gives back. Add @returns {void} when the result is ignored.
  • Fewer parameters are fine. A callback that ignores index can leave it out, as (order) => … does above; JavaScript passes it anyway, and TypeScript allows it.
  • @callback names are types, not values. Use them inside braces in JSDoc. In a .js file a @callback written at the top level of a module can be imported into another file with @import, like a @typedef.

Questions

When should I use @callback instead of an inline function type?

Use @callback when the callback has several parameters, needs descriptions for each, or is used in more than one place. For a one-off with a parameter or two, an inline type such as {(order: Order) => void} is shorter and just as checked.

How do I type a Node.js-style callback with an error first?

Give the first parameter a union with null: @param {Error | null} err and @param {string} [data] in a @callback, or inline {(err: Error | null, data?: string) => void}. Check err before using data.

What if the callback's return value does not matter?

Say void: @returns {void} in the @callback, or => void inline. A function that returns something is still accepted where a void callback is expected; the value is just ignored.

Sources

Last reviewed 21 October 2026.