ConstructorParameters<Type>

Extracts a constructor's argument types as a tuple from a class's static side.

Signature
type ConstructorParameters<Type extends abstract new (...args: any) => any> = Type extends abstract new (...args: infer P) => any ? P : never
  • Added in TypeScript 3.1

Parameters

ParameterTypeDescription
$Typeabstract new (...args: any) => anyA constructor function type — the static side of a class, written `typeof TheClass`, not the instance type `TheClass`. The `abstract` modifier in the constraint means abstract classes are accepted too.

Return value

A tuple type of the constructor's parameter typesA tuple with all the constructor's parameter types, preserving labels, optionality and rest elements; `never` if Type is not a constructor function type.

Errors

A type argument that is not a construct signature errors with "Type 'X' does not satisfy the constraint 'abstract new (...args: any) => any'" — this is what you get from passing the instance type, or the bare `Function` type.

Changelog

  • PHP 4.2Abstract construct signatures were added and the constraint became `abstract new (...args: any) => any`, so `typeof SomeAbstractClass` is now accepted where it previously failed.
  • PHP 4.0Labelled tuple elements mean the result now shows parameter names, e.g. `ConstructorParameters<typeof C>` displaying as `[a: number, b: string]`.

ConstructorParameters is Parameters for construct signatures. It infers the tuple of arguments new expects.

Pass the static side, not the instance

This is the mistake almost everyone makes once. A class name in type position means the instance type — the thing you get back from new, which has no construct signature. The constructor lives on the class object, whose type is typeof TheClass. So it is ConstructorParameters<typeof HttpError>; ConstructorParameters<HttpError> fails the constraint.

When to reach for it

  • Factories. function make<C extends new (...a: any[]) => any>(ctor: C, ...args: ConstructorParameters<C>) forwards arguments to any class with full checking.
  • Dependency registries and pools that record how to construct something and build it later.
  • Test data tables where each row is a valid argument tuple for the class under test.

What trips people up

A class with no explicit constructor still has one — the implicit zero-argument constructor — so the result is [], not an error.

Overloaded constructors collapse to the last signature, exactly as with Parameters.

Private constructors have no accessible construct signature and fail the constraint.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Write a generic factory that takes a class and its constructor arguments and returns an instance.
  • Store construction recipes in a registry or object pool and instantiate them later.
  • Build a table of constructor argument tuples for parameterised tests.

Frequently asked questions

How do I get the constructor argument types of a class?

Use `ConstructorParameters<typeof TheClass>`. The `typeof` is essential — it selects the class object (the static side) that carries the construct signature, rather than the instance type.

Why does ConstructorParameters<MyClass> fail with a constraint error?

Because `MyClass` in type position means the instance type, which has no construct signature and so does not satisfy `abstract new (...args: any) => any`. Write `ConstructorParameters<typeof MyClass>` instead.

Does it work with abstract classes?

Yes, since TypeScript 4.2, when the constraint was changed to `abstract new (...args: any) => any`. Private constructors still fail, because their construct signature is not accessible.

More examples

A generic factory that forwards constructor arguments

Prints `(3, 4)` then `"origin"`. Because `args` is typed `ConstructorParameters<C>` and `C` is inferred from the first argument, the checker validates the remaining arguments against whichever class was passed — the two commented lines are real compile errors. `InstanceType<C>` completes the pair, so `create(Point, 3, 4)` is typed `Point` rather than `any`.

Example · typescript
class Point {
  constructor(public x: number, public y: number) {}
  toString() { return `(${this.x}, ${this.y})`; }
}

class Label {
  constructor(public text: string) {}
  toString() { return `"${this.text}"`; }
}

function create<C extends new (...args: any[]) => any>(
  ctor: C,
  ...args: ConstructorParameters<C>
): InstanceType<C> {
  return new ctor(...args);
}

console.log(String(create(Point, 3, 4)));
console.log(String(create(Label, "origin")));
// create(Point, "3", 4);  -> Argument of type 'string' is not assignable to 'number'
// create(Label);          -> Expected 2 arguments, but got 1

A registry of construction recipes

Prints `primary -> db-1.internal:5432` and `replica -> db-2.internal:5432`, with the keys sorted so the order is stable. Both recipe tuples satisfy the same type because the defaulted `port` parameter becomes an optional tuple element. Change the constructor signature and every recipe literal is re-checked at compile time.

Example · typescript
class Connection {
  constructor(public host: string, public port = 5432) {}
  describe() { return `${this.host}:${this.port}`; }
}

type Recipe = ConstructorParameters<typeof Connection>;
// [host: string, port?: number]

const recipes = new Map<string, Recipe>([
  ["primary", ["db-1.internal", 5432]],
  ["replica", ["db-2.internal"]],
]);

for (const name of [...recipes.keys()].sort()) {
  const recipe = recipes.get(name)!;
  console.log(name, "->", new Connection(...recipe).describe());
}

Discussion

  • Be the first to comment on this lesson.