Recipe · Classes

How do I type static properties and methods in a JavaScript class?

  • Node.js (ES modules)
  • static
  • Classes
  • @static
  • Checked with TypeScript 5.9

The problem My Order class has a tax rate shared by every order and a static fromLines() factory. Do static members need JSDoc of their own, and does the @static tag do anything?

The short answer

Write the static keyword; static fields take their type from their value like any field, and static methods take @param tags like any method:

order.js order.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
export class Order {
  static TAX_RATE = 0.2;
  static #nextNumber = 1001;

  /**
   * @param {string} id
   * @param {number} subtotal Cents.
   */
  constructor(id, subtotal) {
    this.id = id;
    this.subtotal = subtotal;
  }

  get total() {
    return Math.round(this.subtotal * (1 + Order.TAX_RATE));
  }

  /** @param {number[]} lineTotals */
  static fromLines(lineTotals) {
    const id = `A-${Order.#nextNumber++}`;
    return new Order(id, lineTotals.reduce((sum, cents) => sum + cents, 0));
  }
}

const order = Order.fromLines([3900, 1250]);
console.log(order.id, order.total);
export class Order {
  static TAX_RATE = 0.2;
  static #nextNumber = 1001;

  id: string;
  subtotal: number;

  constructor(id: string, subtotal: number) {
    this.id = id;
    this.subtotal = subtotal;
  }

  get total(): number {
    return Math.round(this.subtotal * (1 + Order.TAX_RATE));
  }

  static fromLines(lineTotals: number[]): Order {
    const id = `A-${Order.#nextNumber++}`;
    return new Order(id, lineTotals.reduce((sum, cents) => sum + cents, 0));
  }
}

const order = Order.fromLines([3900, 1250]);
console.log(order.id, order.total);
Static fields, a private static counter and a static factory.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

Order.TAX_RATE is a number, Order.#nextNumber is a counter only the class can see, and Order.fromLines returns an Order, inferred from its return new Order(…).

Why it works

Static members belong to the class object, not to its instances, and TypeScript types them that way: they live on typeof Order, the type of the class itself, while Order names the type of an instance. Inside a static method, this is the class too, so this.TAX_RATE would also work there.

The same thing in TypeScript

The TypeScript equivalent tab is the same class with the instance fields id and subtotal declared and annotated. The static members need no change at all: static is JavaScript syntax.

What goes wrong

Reading a static member through an instance fails, and tsc says where it lives (TS2576). The reverse, an instance property read through the class, is TS2339:

order.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: order.js(12,19): error TS2576: Property 'TAX_RATE' does not exist on type 'Order'. Did you mean to access the static member 'Order.TAX_RATE' instead? order.js(14,19): error TS2339: Property 'id' does not exist on type 'typeof Order'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. order.js(12,19): error TS2576: Property 'TAX_RATE' does not exist on type 'Order'. Did you mean to access the static member 'Order.TAX_RATE' instead? order.js(14,19): error TS2339: Property 'id' does not exist on type 'typeof Order'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. order.js(12,19): error TS2576: Property 'TAX_RATE' does not exist on type 'Order'. Did you mean to access the static member 'Order.TAX_RATE' instead? order.js(14,19): error TS2339: Property 'id' does not exist on type 'typeof Order'. checked 21 October 2026
export class Order {
  static TAX_RATE = 0.2;

  /** @param {string} id */
  constructor(id) {
    this.id = id;
  }
}

const order = new Order("A-1001");
console.log(order.TAX_RATE);error TS2576: Property 'TAX_RATE' does not exist on type 'Order'. Did you mean to access the static member 'Order.TAX_RATE' instead?
console.log(Order.id);error TS2339: Property 'id' does not exist on type 'typeof Order'.
A static member read through an instance, and an instance member through the class.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

Gotchas

  • @static is ignored. The tag does not make anything static; TypeScript does not read it.
  • Static blocks work. A static { … } block runs once when the class is defined, and tsc checks its body like any other code.
  • Static members are inherited. A subclass sees TAX_RATE as its own static property, and can redefine it with a compatible type.

Questions

Does the @static JSDoc tag make a method static?

No. TypeScript ignores @static; only the static keyword makes a member static. A method tagged @static without the keyword is still an instance method, as the @static page shows.

How do I type the class itself, for a function that takes it as an argument?

Use typeof Order: @param {typeof Order} OrderClass accepts the class, with its static members and its constructor. Order alone means an instance.

Sources

Last reviewed 21 October 2026.