How to Define a Generic TypeScript Class Constructor Type
Define a generic class constructor type in TypeScript using the callable signature pattern new (...args: any[]) => T, which the compiler recognizes as a constructor that produces an instance of type T, and optionally prefix with abstract to support abstract base classes.
TypeScript models class constructors as callable signatures that return class instances, with the microsoft/TypeScript repository defining these patterns in its core library files. Understanding how to create generic typescript class constructor types enables you to build flexible factory functions, mixin utilities, and decorator implementations that work with any class structure.
Understanding Constructor Signatures in TypeScript
TypeScript represents constructors through specific callable signatures defined in src/lib/es5.d.ts. The language distinguishes between concrete and abstract instantiation patterns:
new (...args: any[]) => T— A concrete constructor that can be invoked with thenewkeyword to produce an instance of typeT(defined at line 1622)abstract new (...args: any[]) => T— An abstract constructor signature representing classes that cannot be instantiated directly but can be extended (defined at line 1627)
The compiler differentiates these signatures in src/compiler/checker.ts around line 7400, where the type-checking logic enforces that abstract new signatures cannot be called directly while still allowing them in type positions for inheritance.
Creating a Reusable Generic Constructor Type
You can encapsulate constructor signatures into reusable type aliases that accept generic parameters for both the instance type and arguments.
Concrete vs Abstract Constructors
Define a generic constructor type alias for concrete classes:
type Ctor<T = any, A extends any[] = any[]> = new (...args: A) => T;
For abstract classes that serve as base classes, use the abstract modifier:
type AbstractCtor<T = any, A extends any[] = any[]> = abstract new (...args: A) => T;
In these definitions:
Trepresents the instance type created by the constructorAconstrains the tuple of argument types (defaulting toany[])
Leveraging Built-in Utility Types
TypeScript provides built-in utilities in src/lib/es5.d.ts that simplify constructor type definitions without manual signature replication:
ConstructorParameters<T>(line 1627) — Extracts the parameter tuple type from a constructorTInstanceType<T>(line 1637) — Extracts the instance type produced by a constructorT
Combine these to derive constructor types from existing classes:
type CtorFrom<T> = new (...args: ConstructorParameters<T>) => InstanceType<T>;
This approach automatically infers both the arguments and return type from the source class.
Practical Code Examples
Generic Factory Function
Use a constructor type alias to build a factory that instantiates any class:
type Ctor<T, A extends any[] = any[]> = new (...args: A) => T;
class Factory {
static create<T, A extends any[]>(
ctor: Ctor<T, A>,
...args: A
): T {
return new ctor(...args);
}
}
// Usage
class Point {
constructor(public x: number, public y: number) {}
}
const p = Factory.create(Point, 10, 20); // Point { x: 10, y: 20 }
The Ctor alias mirrors the primitive definition found in src/lib/es5.d.ts.
Handling Abstract Classes
When working with abstract base classes, use AbstractCtor to accept both concrete and abstract constructors:
type AbstractCtor<T = any, A extends any[] = any[]> = abstract new (...args: A) => T;
function subclass<TBase extends AbstractCtor>(Base: TBase) {
return class Derived extends Base {
// Additional implementation
};
}
// Example
abstract class Shape {
abstract area(): number;
}
const Circle = subclass(Shape); // Valid: Shape is abstract
This pattern aligns with the compiler's handling of abstract new signatures in src/compiler/checker.ts, allowing abstract classes to appear in type constraints while preventing direct instantiation.
Extracting Types with Utilities
Leverage ConstructorParameters and InstanceType to extract and reuse constructor signatures:
class Box {
constructor(public width: number, public height: number) {}
}
// Resolves to: new (width: number, height: number) => Box
type BoxCtor = new (...args: ConstructorParameters<typeof Box>) => InstanceType<typeof Box>;
These utilities are defined at lines 1627 and 1637 in src/lib/es5.d.ts.
Decorator Metadata Patterns
The decorators library in src/lib/decorators.d.ts (line 27) provides a Class alias for abstract constructors:
type Class<T = any> = abstract new (...args: any[]) => T;
function sealed<T extends Class>(target: T) {
Object.seal(target);
Object.seal(target.prototype);
}
This shorthand represents "any abstract constructor" and is used throughout TypeScript's decorator metadata implementation.
How the TypeScript Compiler Processes Constructors
The distinction between concrete and abstract constructor types is enforced by the type checker in src/compiler/checker.ts around line 7400. This logic ensures that:
- Concrete constructors (
new (...) => T) can be invoked with thenewkeyword - Abstract constructors (
abstract new (...) => T) are valid for subclassing but reject direct instantiation
Test cases in tests/cases/conformance/classes/mixinAbstractClasses.ts demonstrate how these signatures participate in mixin inference, while tests/cases/conformance/override/override19.ts illustrates simple abstract constructor aliases in type-compatibility scenarios.
Summary
- Define generic typescript class constructor types using the
new (...args: A) => Tcallable signature pattern - Prefix with
abstractto support base classes that cannot be instantiated directly - Use
ConstructorParameters<T>andInstanceType<T>fromsrc/lib/es5.d.tsto extract constructor components from existing types - Reference
src/lib/decorators.d.tsfor theClass<T>alias when working with decorator metadata - Remember that
src/compiler/checker.tsenforces the distinction between concrete and abstract constructor signatures
Frequently Asked Questions
What is the difference between new and abstract new in TypeScript?
The new signature represents a concrete constructor that can be called with the new keyword to create instances, while abstract new represents an abstract constructor that can only be used for typing and subclassing. According to the TypeScript source code in src/compiler/checker.ts, the compiler treats these differently by prohibiting direct instantiation of abstract constructors while allowing them in extends clauses.
How do I extract constructor parameter types in TypeScript?
Use the built-in utility type ConstructorParameters<T> defined in src/lib/es5.d.ts at line 1627. This utility extracts the parameter tuple type from a constructor type, allowing you to reference the arguments required to instantiate a class without manually redefining them.
Can I use generic constructor types with mixins?
Yes, generic constructor types are essential for mixin patterns. The test file tests/cases/conformance/classes/mixinAbstractClasses.ts in the TypeScript repository demonstrates how abstract constructor signatures enable mixin functions to accept both concrete and abstract base classes while preserving type inference for the resulting composed class.
Where are the built-in constructor utility types defined?
The core constructor utility types ConstructorParameters and InstanceType are defined in src/lib/es5.d.ts at lines 1627 and 1637 respectively. Additionally, the Class type alias used for decorator metadata is defined in src/lib/decorators.d.ts at line 27 as an abstract constructor signature.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s https://instagit.com/install.md