Type expression

Generic classes

  • Generics
  • Tested with TypeScript 5.9

Put @template T in the class's JSDoc comment, and T is available in every member's tags: field types, @param, @returns. It is the JavaScript spelling of class Queue<T>.

JavaScript cannot write new Queue<Job>(), so the type argument comes from one of two places: the variable's @type {Queue<Job>}, as in the first example, or the constructor's arguments, which TypeScript infers it from, as in the second.

At a glance

Use JSDoc in a .js file TypeScript in a .ts file
A generic class /** @template T */ class Queue { /** @param {T} item */ push(item) {} } class Queue<T> { push(item: T) {} }
An instance /** @type {Queue<Job>} */ const jobs = new Queue(); const jobs = new Queue<Job>();

Examples

queue.js queue.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
/** @template T */
export class Queue {
  /** @type {T[]} */
  #items = [];

  /** @param {T} item */
  push(item) {
    this.#items.push(item);
  }

  /** @returns {T | undefined} */
  shift() {
    return this.#items.shift();
  }
}

/** @type {Queue<{ to: string, subject: string }>} */
const outbox = new Queue();
outbox.push({ to: "jane@example.com", subject: "Your order shipped" });
outbox.shift()?.subject;
export class Queue<T> {
  #items: T[] = [];

  push(item: T) {
    this.#items.push(item);
  }

  shift(): T | undefined {
    return this.#items.shift();
  }
}

const outbox = new Queue<{ to: string; subject: string }>();
outbox.push({ to: "jane@example.com", subject: "Your order shipped" });
outbox.shift()?.subject;
A generic class with @template.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}
box.js 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
/** @template T */
export class Labeled {
  /**
   * @param {string} label
   * @param {T} value
   */
  constructor(label, value) {
    this.label = label;
    this.value = value;
  }
}

const price = new Labeled("price", 19.5);
price.value.toFixed(2);
const sku = new Labeled("sku", "TEE-01");
sku.value.toLowerCase();
The constructor's arguments can set the type parameter.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}
queue.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: queue.js(16,11): error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. queue.js(16,11): error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. queue.js(16,11): error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'. checked 21 October 2026
/** @template T */
export class Queue {
  /** @type {T[]} */
  items = [];

  /** @param {T} item */
  push(item) {
    this.items.push(item);
  }
}

/** @type {Queue<string>} */
const skus = new Queue();
skus.push("TEE-01");
skus.push(42);error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
The type argument is checked on every method.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

Without a type argument

If neither the variable nor the constructor's arguments say what T is, it becomes any in a .js file, and the instance checks nothing. Give the variable a type, add a default with @template [T=…], or pass something the constructor can infer from.

Subclasses

A subclass of a generic class supplies the type argument with @extends: /** @extends {Queue<string>} */ class SkuQueue extends Queue {}. A generic subclass combines @template T with @extends {Queue<T>}.

Gotchas

  • Put the field's JSDoc on its own line above it; a comment on the same line as the class's opening brace is not attached to the member.
  • Static members cannot use the class's type parameter, as in TypeScript.

Questions

How do I make a JavaScript class generic with JSDoc?

Add @template T to the class comment and use T in the members' JSDoc. Create instances with a typed variable, /** @type {Queue<string>} */ const q = new Queue();.

Sources

Last reviewed 20 October 2026.