49

The type signature for a non-abstract class (non-abstract constructor function) in TypeScript is the following:

declare type ConstructorFunction = new (...args: any[]) => any;

This is also called a newable type. However, I need a type signature for an abstract class (abstract constructor function). I understand it can be defined as having the type Function, but that is way too broad. Isn't there a more precise alternative?


Edit:

To clarify what I mean, the following little snippet demonstrates the difference between an abstract constructor and a non-abstract constructor:

declare type ConstructorFunction = new (...args: any[]) => any;

abstract class Utilities {
    ...
}

var UtilityClass: ConstructorFunction = Utilities; // Error.

Type 'typeof Utilities' is not assignable to type 'new (...args: any[]) => any'.

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

2
  • Can you elaborate more? Maybe provide more code? Why is there a difference in this case between abstract and non-abstract classes? The ctor is never abstract. Commented Apr 27, 2016 at 9:55
  • 2
    @NitzanTomer I appreciate your help. I've edited a minimal reproducible example into the question to demonstrate the problem. Basically, I need to pass a class as an argument. The type ConstructorFunction works for non-abstract classes, but not for abstract ones. Commented Apr 27, 2016 at 10:13

6 Answers 6

55

Was just struggling with a similar problem myself, and this seems to work for me:

type Constructor<T> = Function & { prototype: T }
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks, this does exactly what is needed. @cheez - This describes a Function with a prototype - so instead of requiring that the type defines a constructor, this defines that it defines the prototype. Which is what an abstract class is - no constructor, just the prototype.
Could you please share an example of this making a class with an abstract constructor. Perhaps including extending the class?
@Knagis could one of you please elaborate on this answer. I'd really like to be able to use it, but the way it's shown above is not a complete enough answer for me to understand.
Above queries answered in follow-up: stackoverflow.com/questions/52349758/…
38

As of TypeScript 4.2, you can use an abstract constructor type:

abstract class Utilities {
    abstract doSomething(): void;
}

type ConstructorFunction = abstract new (...args: any[]) => any;
var UtilityClass: ConstructorFunction = Utilities; // ok!

4 Comments

This is now the best answer, imo.
nice, but does not work with protected constructors in typescript 4.9.4: Playground
@TmTron Indeed, at the moment you can't have a visibility modifier on a new() type. See github.com/microsoft/TypeScript/issues/30991
Been looking for hours for this answer... turns out my code was correct, but my TS version wasn't. And the IDE wasn't even complaining, only the compiler
4

The whole point with abstract classes (in OO in general) is that you can not instantiate them, you need a concrete non-abstract implementation.

I assume that you want to have different implementations to that abstract class and want to be able to receive one of those implementations (as a parameter or something of the likes).
If that's the case, then maybe this might solve your problem:

declare type ConstructorFunction<T extends Utilities> = new (...args: any[]) => T;

abstract class Utilities { }

class MyUtilities extends Utilities { }

var UtilityClass: ConstructorFunction<MyUtilities> = MyUtilities; 

5 Comments

Unfortunately, I need to pass an abstract class as an argument, which is possible if the parameter type is any or Function. However, these types allow objects other than abstract classes to be passed as well, hence my need for a type definition especially for abstract classes.
@JohnWhite Why do you need to pass an abstract class? That's a weird scenario
Yeah, it is. It's for a custom serialization/deserialization engine where it's possible to annotate properties with decorators. Member polymorhpism is supported, and therefore an abstract class can be specified for a property type, which is then passed into a metadata structure. Runtime type checks are in place currently, but it would be significantly better with type-safety.
@JohnWhite if I get you right then you only need that abstract class to get metadata from it. If that's the case then just get it as any because I don't see the benefits of having the class type. Unless I'm missing something, if that's the case then you probably should explain the scenario better to get real help.
I know this is old but I'm running into the same issue, I'm using polymorphism to store the child classes of a certain class in a map, but can't instantiate them because the parent is marked as abstract. I'd like type-safety on the constructor arguments as the spec is still in flux but can't have it because the compiler errors.
3

Having the same problem. I guess, an essence of abstract class constructor signature is an absense of new ( ... ) : X thingy in its declaration. That's why it can be declared explicitly.

However. You can do this, and it will compile.

var UtilityClass: typeof Utilities  = Utilities;

typeof Something is a nice way to reference constructor types, however, it cannot be extended.

And in any case you can do thing like this:

var UtilityClass: ConstructorFunction = <any> Utilities;

Comments

0

This solution:

type Constructor<T> = Function & { prototype: T }

won't allow you to create instances of this type using new keyword.

There's another simple solution:

type Constructor = new (...args: any[]) => AbstractClass

((Where AbstractClass is a name of your AbstractClass))

1 Comment

And what is the AbstractClass in this context ?
0

Thanks to @TehauCave's answer, which was my starting point, I could figure out a way to define a type that also cares about parameter types.

export type Constructor<T, TArgs extends any[] = any> = Function & {
    prototype: T,
    apply: (this: any, args: TArgs) => void
};

The above type can be used either

  • regardless of parameter types:

    Constructor<YourAbstractClass>
    

or

  • restricting parameter types:

    Constructor<YourAbstractClass, [number, string, boolean]>
    

Using the Constructor type, it is also possible to infer the parameter types of a given constructor:

type ConstructorParameters<T extends Constructor<any>> = T extends Constructor<any, infer TParams> ? TParams : never;

and then,

ConstructorParameters<YourAbstractClass> // e.g., [number, string, boolean]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.