How to fix Cannot assign an abstract constructor type to a non-abstract constructor type in TypeScript

TypeScriptINTERMEDIATEMEDIUMUpdated Dec 8, 2025· 118 views

This error occurs when attempting to assign or pass an abstract class to a function parameter that expects a concrete constructor. TypeScript prevents this because abstract classes cannot be instantiated directly. The solution involves using the abstract constructor type syntax (available in TypeScript 4.2+) to explicitly allow abstract constructors in function signatures.

Quick fix

Fixes Cannot assign an abstract constructor type to a non-abstract constructor type

// Non-abstract - expects concrete class only
function createInstance(Ctor: new () => Animal) {
  return new Ctor(); // This calls the constructor directly
}

// ❌ This fails with error
abstract class AnimalBase {
  abstract sound: string;
}

const result = createInstance(AnimalBase); // ERROR: Cannot assign abstract constructor
  1. Understand the difference between abstract and non-abstract constructor signatures
  2. Add abstract modifier to constructor signature in function parameter (TypeScript 4.2+)
  3. Define a reusable abstract constructor type
  4. Use abstract constructor types with dependency injection
typescript — error
// Non-abstract - expects concrete class only
function createInstance(Ctor: new () => Animal) {
return new Ctor(); // This calls the constructor directly
}
// ❌ This fails with error
abstract class AnimalBase {
abstract sound: string;
}
const result = createInstance(AnimalBase); // ERROR: Cannot assign abstract constructor
Cannot assign an abstract constructor type to a non-abstract constructor type

What this error means

This error reflects TypeScript's strict type system for constructor signatures. When a function defines a parameter expecting a constructor type like `new () => SomeClass`, TypeScript assumes only concrete classes can be passed because the constructor is meant to be called directly to create instances. Abstract classes cannot be instantiated, so TypeScript prevents passing them to such signatures. The error commonly arises in scenarios involving dependency injection, factory functions, class decorators, or utility functions that work with constructors. TypeScript is ensuring type safety by preventing code that would fail at runtime—you cannot call `new` on an abstract class, even though you can pass abstract classes to other parts of your code. TypeScript 4.2 introduced support for abstract constructor signatures (using the `abstract new` syntax) to solve this problem, allowing developers to explicitly signal that a parameter accepts both abstract and concrete constructors.

Typical symptoms

  • TypeScript compiler error when passing an abstract class to a function expecting a constructor
  • Error appears when using abstract classes with dependency injection decorators like @injectable()
  • Red squiggly lines in VS Code when trying to assign an abstract class to a variable typed as a constructor
  • Build fails with "Cannot assign an abstract constructor type" during TypeScript compilation
  • Error when using ConstructorParameters<typeof AbstractClass>
  • Class decorators fail to apply to abstract classes with error about constructor type mismatch
  • Factory functions or utilities that accept class constructors reject abstract classes

Common causes

  1. Function signature declares constructor type without abstract modifier (e.g., `Ctor: new () => Base`)
  2. Passing an abstract class to a function that expects a concrete constructor type
  3. Using utility types like ConstructorParameters or InstanceType with abstract classes
  4. Applying decorators to abstract classes when decorator expects non-abstract constructor
  5. TypeScript version is older than 4.2 (abstract constructor types not supported)
  6. Type definition from external library uses non-abstract constructor signature
  7. Mixin pattern or higher-order functions expecting only concrete constructors

How to fix "Cannot assign an abstract constructor type to a non-abstract constructor type"

1Understand the difference between abstract and non-abstract constructor signatures

A non-abstract constructor signature expects a concrete class that can be instantiated:

typescript
// Non-abstract - expects concrete class only
function createInstance(Ctor: new () => Animal) {
  return new Ctor(); // This calls the constructor directly
}

// ❌ This fails with error
abstract class AnimalBase {
  abstract sound: string;
}

const result = createInstance(AnimalBase); // ERROR: Cannot assign abstract constructor

An abstract constructor signature allows both abstract and concrete classes:

typescript
// Abstract - accepts both abstract and concrete
function checkType(Ctor: abstract new () => Animal) {
  console.log(Ctor.name); // Just checks the constructor, doesn't call it
}

// ✅ This works with both
checkType(AnimalBase);  // Abstract class - OK
checkType(Dog);         // Concrete class - OK
2Add abstract modifier to constructor signature in function parameter (TypeScript 4.2+)

If your function doesn't actually need to instantiate the class (just reference it), use the abstract new syntax:

typescript
// Before (fails with abstract classes)
function processClass(Ctor: new () => Base) {
  return Ctor.name;
}

// After (accepts both abstract and concrete)
function processClass(Ctor: abstract new () => Base) {
  return Ctor.name; // Now works with abstract classes
}

abstract class AnimalBase {
  abstract makeSound(): void;
}

class Dog extends AnimalBase {
  makeSound() { console.log("Woof"); }
}

processClass(AnimalBase); // ✅ Works
processClass(Dog);        // ✅ Works
3Define a reusable abstract constructor type

For cleaner code, define a type alias for abstract constructors and use it throughout your codebase:

typescript
// Define once, use everywhere
type AbstractConstructor<T = {}> = abstract new (...args: any[]) => T;
type Constructor<T = {}> = new (...args: any[]) => T;

// Function that needs abstract classes
function mixinClass<T extends Constructor>(Base: T) {
  return class extends Base {
    constructor(...args: any[]) {
      super(...args);
      console.log("Mixin applied");
    }
  };
}

// Function that just inspects classes
function getClassName(Ctor: AbstractConstructor) {
  return Ctor.name;
}

getClassName(SomeAbstractClass); // ✅ Works
getClassName(SomeConcreteClass);  // ✅ Works
4Use abstract constructor types with dependency injection

When working with DI decorators or frameworks, define constructor types to accept abstract classes:

typescript
// Type definition for service constructor
type ServiceConstructor = abstract new (...args: any[]) => any;

// Decorator that works with both abstract and concrete services
function Service(config: any) {
  return function <T extends ServiceConstructor>(constructor: T) {
    // Register service, metadata, etc.
    console.log(`Registered ${constructor.name}`);
    return constructor;
  };
}

// Now this works with abstract classes
@Service({ scope: "transient" })
abstract class BaseUserService {
  abstract getUser(id: string): Promise<User>;
}

// And concrete implementations
@Service({ scope: "transient" })
class UserService extends BaseUserService {
  getUser(id: string) {
    return Promise.resolve({ id, name: "John" });
  }
}
5Update type definitions from external libraries

If you're using a library whose type definitions use non-abstract constructor signatures, you have options:

Option 1: Use a type assertion (temporary workaround)

typescript
function registerClass(Ctor: new () => Base) {
  // ...
}

// Bypass the type checker
registerClass(AbstractClass as any);

Option 2: Create a wrapper function

typescript
type AbstractConstructor<T> = abstract new (...args: any[]) => T;

function registerAbstractClass<T>(Ctor: AbstractConstructor<T>) {
  // Convert to expected type
  return registerClass(Ctor as any);
}

registerAbstractClass(MyAbstractClass); // ✅ Type-safe wrapper

Option 3: Report issue to library maintainers
File an issue or pull request to update the library's type definitions to use abstract constructor types.

6Apply to higher-order functions and mixins

When creating functions that wrap or enhance classes, use abstract constructor types:

typescript
type AbstractConstructor<T = {}> = abstract new (...args: any[]) => T;

// Higher-order function that wraps a class
function withLogging<T extends AbstractConstructor>(
  Base: T
): T {
  return class extends Base as any {
    constructor(...args: any[]) {
      super(...args);
      console.log(`${Base.name} instantiated`);
    }
  } as T;
}

// Abstract class to wrap
abstract class Repository {
  abstract find(id: string): any;
}

// Concrete implementation
class UserRepository extends Repository {
  find(id: string) {
    return { id, name: "User" };
  }
}

// Apply wrapper to both abstract and concrete
const WrappedAbstractRepo = withLogging(Repository);  // ✅ Works
const WrappedConcreteRepo = withLogging(UserRepository); // ✅ Works
7Check TypeScript version and update if needed

The abstract constructor type syntax requires TypeScript 4.2 or later. Check your current version:

bash
tsc --version

If you're on an older version, upgrade TypeScript:

bash
# npm
npm install --save-dev typescript@latest

# yarn
yarn add -D typescript@latest

# pnpm
pnpm add -D typescript@latest

Verify the upgrade by checking the version again and recompiling your project. After upgrading, you can use the abstract new syntax in your code.

Advanced notes

The abstract constructor type is a subtle but important distinction in TypeScript's type system. When you declare a function parameter as new () => T, the compiler infers that the function will call new on the constructor. Since abstract classes cannot be instantiated (they cannot be called with new), TypeScript rejects abstract classes. Using abstract new () => T signals that the function won't call the constructor directly—it just references the constructor type.

This pattern is especially useful in the mixin pattern, where a function takes a class constructor, extends it, and returns the extended class. The original constructor is never called with new, just referenced and extended. Similarly, in dependency injection frameworks, containers often just register constructors without immediately instantiating them.

The distinction between new () => T and abstract new () => T is purely a type-checking construct—it doesn't exist in the compiled JavaScript. This is a design pattern borrowed from other languages' type systems to provide more precise control over what can be passed to functions.

For complex inheritance hierarchies, ensure you understand that a function accepting abstract new () => Base can receive any class that extends Base, whether Base itself is abstract or concrete. This provides maximum flexibility while maintaining type safety.

When working with libraries that have strict constructor types, consider whether type assertions or wrapper functions are appropriate trade-offs. If a library is widely used, contributing a fix upstream often benefits the entire community.

Frequently asked questions

What does "Cannot assign an abstract constructor type to a non-abstract construct" mean?
This error reflects TypeScript's strict type system for constructor signatures. When a function defines a parameter expecting a constructor type like new () => SomeClass, TypeScript assumes only concrete classes can be passed because the constructor is meant to be called directly to create instances. Abstract classes cannot be instantiated, so TypeScript prevents passing them to such signatures.
How do I fix "Cannot assign an abstract constructor type to a non-abstract construct"?
This error occurs when attempting to assign or pass an abstract class to a function parameter that expects a concrete constructor. TypeScript prevents this because abstract classes cannot be instantiated directly. The solution involves using the abstract constructor type syntax (available in TypeScript 4.2+) to explicitly allow abstract constructors in function signatures.
What causes "Cannot assign an abstract constructor type to a non-abstract construct"?
Function signature declares constructor type without abstract modifier (e.g., Ctor: new () => Base) Passing an abstract class to a function that expects a concrete constructor type Using utility types like ConstructorParameters or InstanceType with abstract classes
Is the "Cannot assign an abstract constructor type to a non-abstract construct" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading