ConstructorParameters<Type>
Extracts a constructor's argument types as a tuple from a class's static side.
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
| Parameter | Type | Description |
|---|---|---|
$Type | abstract new (...args: any) => any | A 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
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`.
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 1A 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.
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