'How to refer to a class with private constructor in a function with generic type parameters?

I have a base class and multiple classes that extend that base class which all have a private constructor.

I now want to create a static function that creates new instances of the extending classes. Normally I would have a function header of something like this:

public static createInstance<T extends BaseClass>(clazz: { new(): T }): T

When I want to use this function:

createInstance(CustomClass)

It causes typescript to spit out this error:

TS2345: Argument of type 'typeof CustomClass' is not assignable to parameter of type 'new () => CustomClass'. Cannot assign a 'private' constructor type to a 'public' constructor type.

The code actual transpiled code is working perfectly fine.

I know what this error is telling me. But I cannot find a way around this. I've searched a lot and I seem to be very alone with this problem. Is there any way to reference classes with private constructors?



Solution 1:[1]

The solution to your issue is surprizingly simple: instead of constraining the generic type parameter T to an instance type of the Base class, constrain it to the static (or constructor) side of it via the typeof Base type query.

This will ensure that clazz will be inferred as the constructor type of the concrete type of T instead of the public constructor type (new (): T), effectively bypassing the private constructor check while retaining the benefits of the generic constraint:

abstract class Abstract {}

class Base { answer = 42; }

class Custom extends Base {
  private constructor() { super(); }
}

class Unrelated {}

declare function createInstance<T extends typeof Base>(clazz: T): T;

createInstance(Abstract); // error, OK
createInstance(Base); // OK
createInstance(Custom); // OK
createInstance(Unrelated); // error, OK

Playground

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Oleg Valter is with Ukraine