47

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.

John Weisz
  • 30,137
  • 13
  • 89
  • 132
  • 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. – Nitzan Tomer Apr 27 '16 at 09:55
  • 2
    @NitzanTomer I appreciate your help. I've edited a [mcve] 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. – John Weisz Apr 27 '16 at 10:13

6 Answers6

54

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

type Constructor<T> = Function & { prototype: T }
Tehau Cave
  • 588
  • 5
  • 3
  • 8
    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. – Knaģis May 10 '17 at 08:40
  • 5
    Could you please share an example of this making a class with an abstract constructor. Perhaps including extending the class? – Seph Reed Aug 10 '18 at 14:38
  • 18
    @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. – Seph Reed Sep 15 '18 at 23:30
  • Above queries answered in follow-up: https://stackoverflow.com/questions/52349758/how-does-type-constructort-function-prototype-t-apply-to-abstract-c/52350147#52350147 – zr0gravity7 Jan 24 '23 at 20:04
34

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!
Mattias Buelens
  • 19,609
  • 4
  • 45
  • 51
  • 3
    This is now the best answer, imo. – JoshuaCWebDeveloper Aug 27 '21 at 19:43
  • nice, but does not work with protected constructors in typescript 4.9.4: [Playground](https://www.typescriptlang.org/play?#code/IYIwzgLgTsDGEAJYBthjAgqhAlsnuAphgN4BQClCokM8CAJgPYDKTAtoRABY4B2AcwAUASgBcCAG5McDANxkAvmTIQAngAdCCAMJM+tAK7wmUAGKG+8HPoQBeauGhxEfQgHcEQgHS-gUATAJYD41AG0AXRF7AD5qUIVJfyxcfHUdVHQJPQNoYwhTCytcWwdsPAIcYjkEAHpahCYAawBCFRpnehQ0DDMmJgRyKgQNKCYIQnhCBiR9IxMoUUHlZVqAKjWKNYR1LTBYKBwNRAAWbwBObxOthAAVTW0Acl3CJgAzBD6mR4QcDD5xtR0DgBHxQMhtAUdg8EI8cvMCuZLNZ9I9vDdKDoQgDED0QXxqLDRuNJhMGD9YHM8gtoVodgNgETDCB8LAKVSoPlTLTCOiEGtamRKbkEG9+jpEdkOVykcUbASHF8FEA) – TmTron Dec 15 '22 at 15:24
  • @TmTron Indeed, at the moment you can't have a visibility modifier on a `new()` type. See https://github.com/microsoft/TypeScript/issues/30991 – Mattias Buelens Dec 18 '22 at 07:53
3

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; 
Nitzan Tomer
  • 155,636
  • 47
  • 315
  • 299
  • 2
    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. – John Weisz Apr 27 '16 at 11:14
  • @JohnWhite Why do you need to pass an abstract class? That's a weird scenario – Nitzan Tomer Apr 27 '16 at 11:17
  • 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. – John Weisz Apr 27 '16 at 11:50
  • @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. – Nitzan Tomer Apr 27 '16 at 12:19
  • 1
    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. – Griffork Mar 03 '20 at 07:20
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;
gaperton
  • 3,566
  • 1
  • 20
  • 16
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))

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]
Davide Cannizzo
  • 2,826
  • 1
  • 29
  • 31