Type expression

Constructor types

  • Functions
  • Tested with TypeScript 5.9

When a function takes a class as an argument, the parameter's type is a constructor type: {new (sku: string) => T} means "something you can call with new and a string, which produces a T". Any class with a compatible constructor fits, and the result of new Kind(…) is typed T.

{typeof Shirt} is the constructor type of one particular class, static members included. Use it when the code needs Shirt's statics, such as Kind.category in the second example.

At a glance

Use JSDoc in a .js file TypeScript in a .ts file
Something you call with new /** @param {new (sku: string) => Shirt} Kind */ (Kind: new (sku: string) => Shirt)
A specific class /** @param {typeof Shirt} Kind */ (Kind: typeof Shirt)
Closure spelling {function(new: Shirt, string)} new (sku: string) => Shirt

Examples

registry.js registry.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
 * @param {new (sku: string) => T} Kind
 * @param {string[]} skus
 * @returns {T[]}
 */
export function createAll(Kind, skus) {
  return skus.map((sku) => new Kind(sku));
}

class Shirt {
  /** @param {string} sku */
  constructor(sku) {
    this.sku = sku;
    this.size = "M";
  }
}

const shirts = createAll(Shirt, ["TEE-01", "TEE-02"]);
shirts[0].size;
export function createAll<T>(Kind: new (sku: string) => T, skus: string[]): T[] {
  return skus.map((sku) => new Kind(sku));
}

class Shirt {
  sku: string;
  size: string;

  constructor(sku: string) {
    this.sku = sku;
    this.size = "M";
  }
}

const shirts = createAll(Shirt, ["TEE-01", "TEE-02"]);
shirts[0].size;
Passing a class as a value with a constructor type.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}
registry.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
export class Shirt {
  /** @param {string} sku */
  constructor(sku) {
    this.sku = sku;
  }

  static category = "apparel";
}

/** @param {typeof Shirt} Kind */
export function describe(Kind) {
  return `${Kind.category}: ${new Kind("TEE-01").sku}`;
}

describe(Shirt);
typeof a class is its constructor type.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}
registry.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: registry.js(11,11): error TS2345: Argument of type '(sku: string) => { sku: string; }' is not assignable to parameter of type 'new (sku: string) => { sku: string; }'. Type '(sku: string) => { sku: string; }' provides no match for the signature 'new (sku: string): { sku: string; }'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. registry.js(11,11): error TS2345: Argument of type '(sku: string) => { sku: string; }' is not assignable to parameter of type 'new (sku: string) => { sku: string; }'. Type '(sku: string) => { sku: string; }' provides no match for the signature 'new (sku: string): { sku: string; }'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. registry.js(11,11): error TS2345: Argument of type '(sku: string) => { sku: string; }' is not assignable to parameter of type 'new (sku: string) => { sku: string; }'. Type '(sku: string) => { sku: string; }' provides no match for the signature 'new (sku: string): { sku: string; }'. checked 21 October 2026
/**
 * @param {new (sku: string) => { sku: string }} Kind
 */
export function createOne(Kind) {
  return new Kind("TEE-01");
}

/** @param {string} sku */
const makeShirt = (sku) => ({ sku });
createOne(makeShirt);error TS2345: Argument of type '(sku: string) => { sku: string; }' is not assignable to parameter of type 'new (sku: string) => { sku: string; }'.
  Type '(sku: string) => { sku: string; }' provides no match for the signature 'new (sku: string): { sku: string; }'.
A plain function is not a constructor.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

Generic factories

Combine a constructor type with @template T to keep the instance type: createAll(Shirt, skus) returns Shirt[], so shirts[0].size is checked. That pattern is common for registries, dependency injection and test helpers.

Plain functions do not fit

An arrow function or a factory function cannot be called with new, so passing one where a constructor type is expected is TS2345, as the third example shows. If both should be accepted, take a function type instead and call it without new.

Gotchas

  • In a .ts file an abstract class cannot be created with new, so it does not fit a new (…) => T parameter; JavaScript has no abstract classes, so this only matters for classes declared in .d.ts files.
  • The Closure form {function(new: Shirt, string)} is read as a constructor type that takes a string and creates a Shirt.

Questions

How do I type a class passed as a parameter in JSDoc?

With a constructor type, {new (...args: any[]) => T}, or with {typeof MyClass} for one specific class.

Sources

Last reviewed 20 October 2026.