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 the new keyword to produce an instance of type T (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:

  • T represents the instance type created by the constructor
  • A constrains the tuple of argument types (defaulting to any[])

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 constructor T
  • InstanceType<T> (line 1637) — Extracts the instance type produced by a constructor T

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 the new keyword
  • 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) => T callable signature pattern
  • Prefix with abstract to support base classes that cannot be instantiated directly
  • Use ConstructorParameters<T> and InstanceType<T> from src/lib/es5.d.ts to extract constructor components from existing types
  • Reference src/lib/decorators.d.ts for the Class<T> alias when working with decorator metadata
  • Remember that src/compiler/checker.ts enforces 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:

Share the following with your agent to get started:
curl -s https://instagit.com/install.md

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client