This error occurs when a class's constructor signature doesn't match the constructor signature expected by a type or interface. TypeScript is strict about constructor compatibility when using classes with type-based constructor signatures.
Fixes TS2322
// If you see this error:
// Type 'MyClass' is not assignable to type 'ConstructorType'
// Constructor of class 'MyClass' is not compatible with
// constructor signature of type 'ConstructorType'
type ConstructorType = new (x: string, y: number) => any;
class MyClass {
constructor(x: number) {
// Parameter order/types don't match
}
}
const ctor: ConstructorType = MyClass; // ERROR// If you see this error:// Type 'MyClass' is not assignable to type 'ConstructorType'// Constructor of class 'MyClass' is not compatible with// constructor signature of type 'ConstructorType'type ConstructorType = new (x: string, y: number) => any;class MyClass {constructor(x: number) {// Parameter order/types don't match}}const ctor: ConstructorType = MyClass; // ERRORConstructor of class 'X' is not compatible with constructor signature of type 'Y'
TypeScript has a unique constraint: classes have both an instance side and a static side. The instance side contains instance properties and methods, while the static side contains the constructor. When you define an interface with a constructor signature (using the `new` keyword) and try to use a class to satisfy that interface, TypeScript checks whether the class constructor matches. Similarly, if you assign a class to a variable that expects a specific constructor signature, TypeScript enforces compatibility. The key issue is that when you `implements` an interface, TypeScript only checks the instance side of the class, not the constructor. This is why you can have a class that implements an interface but still fail the constructor signature check. This error typically appears when: - A class is assigned to a type that expects a different constructor signature - A class constructor has different parameter types, order, or optionality than expected - You're using the class as a factory function in code that expects a specific constructor shape
First, identify what type signature the class needs to match. Look at the error message carefully:
// If you see this error:
// Type 'MyClass' is not assignable to type 'ConstructorType'
// Constructor of class 'MyClass' is not compatible with
// constructor signature of type 'ConstructorType'
type ConstructorType = new (x: string, y: number) => any;
class MyClass {
constructor(x: number) {
// Parameter order/types don't match
}
}
const ctor: ConstructorType = MyClass; // ERRORThe error tells you exactly what the expected constructor signature is.
Modify the class constructor to match the expected parameter types and order:
type ConstructorType = new (x: string, y: number) => any;
class MyClass {
constructor(x: string, y: number) {
// Now matches the expected signature
}
}
const ctor: ConstructorType = MyClass; // OKMake sure:
- Parameter names match (TypeScript checks this)
- Parameter types match exactly
- Parameter order matches
- Optional parameters are in the right places
If the expected constructor has optional parameters, match that in your class:
// Expected: optional parameter
type ConstructorType = new (x?: string) => any;
class MyClass {
constructor(x?: string) {
// Correctly matches optional parameter
}
}
const ctor: ConstructorType = MyClass; // OK
// For rest parameters:
type VarConstructor = new (...args: any[]) => any;
class VarClass {
constructor(...args: any[]) {
// Correctly matches variadic parameters
}
}
const varCtor: VarConstructor = VarClass; // OKIf you're trying to create a factory pattern, split the interface into constructor and instance types:
// WRONG: Trying to implement constructor signature directly
interface ClockConstructor {
new (hour: number, minute: number): Clock;
}
// Can't do this - class implements only the instance side
class Clock implements ClockConstructor {
constructor(h: number, m: number) {}
tick() {}
}
// RIGHT: Create separate types
interface Clock {
tick(): void;
hour: number;
minute: number;
}
interface ClockConstructor {
new (hour: number, minute: number): Clock;
}
class Clock implements Clock {
hour: number;
minute: number;
constructor(h: number, m: number) {
this.hour = h;
this.minute = m;
}
tick() {
this.minute++;
}
}
// Now you can use Clock as a constructor
const ctor: ClockConstructor = Clock; // OKRemember that constructor parameters are contravariant. A constructor accepting a broader type can't be used where a stricter type is expected:
type ConstructorType = new (x: string | number) => any;
class MyClass {
// WRONG: Requires a string, but constructor type accepts string | number
constructor(x: string) {
// Parameter type is too strict
}
}
const ctor: ConstructorType = MyClass; // ERROR
// CORRECT: Accept the same or broader type
class MyClass {
constructor(x: string | number | boolean) {
// Parameter type is equal to or broader
}
}
const ctor: ConstructorType = MyClass; // OKAfter fixing the constructor signature, verify your class works as expected:
type ConstructorType = new (x: string) => { getValue(): string };
class MyClass {
private value: string;
constructor(x: string) {
this.value = x;
}
getValue() {
return this.value;
}
}
// Use the constructor
const ctor: ConstructorType = MyClass;
const instance = new ctor("hello");
console.log(instance.getValue()); // "hello"
// Verify the instance
if (instance instanceof MyClass) {
console.log("Instance created successfully");
}### Mixin Patterns and Constructor Constraints
When using mixin patterns, the type parameter must be constrained to a mixin constructor type:
type Constructor = new (...args: any[]) => {};
function Timestamped<T extends Constructor>(Base: T) {
return class extends Base {
timestamp = Date.now();
};
}
// Works: constructor accepts any arguments
class User {
constructor(name: string) {}
}
const TimestampedUser = Timestamped(User); // OK### Constructor Return Type Mismatch
The error TS2203 (Construct signature return types differ) occurs when constructors return different types:
type ConstructorA = new () => { x: number };
type ConstructorB = new () => { x: string };
// Can't assign ConstructorB to ConstructorA
// because return types don't match### Static vs Instance Side
Remember that implements checks only the instance side:
interface MyInterface {
instanceMethod(): void;
}
interface MyStaticInterface {
new (): MyInterface;
staticMethod(): void;
}
class MyClass implements MyInterface {
instanceMethod() {}
static staticMethod() {}
}
// This checks only instance side (OK)
const instance: MyInterface = new MyClass();
// This checks constructor and static side (may fail)
const ctor: MyStaticInterface = MyClass as any;