114

In Java, you can give a class to a method as a parameter using the type "Class". I didn't find anything similar in the typescript docs - is it possible to hand a class to a method? And if so, does the type "any" include such class-types?

Background: I'm having an issue with Webstorm, telling me that I cannot hand over a class to @ViewChild(...) in Angular 2. However, the Typescript compiler does not complain. The signature of @ViewChild() seems to be "Type<any> | Function | string", so I wonder if any includes Classes or not.

Community
  • 1
  • 1
Florian Gössele
  • 4,376
  • 7
  • 25
  • 49

8 Answers8

98

The equivalent for what you're asking in typescript is the type { new(): Class }, for example:

class A {}

function create(ctor: { new(): A }): A {
    return new ctor();
}

let a = create(A); // a is instanceof A

(code in playground)

The code above will allow only classes whose constructor has no argument. If you want any class, use new (...args: any[]) => Class

Mohayemin
  • 3,841
  • 4
  • 25
  • 54
Nitzan Tomer
  • 155,636
  • 47
  • 315
  • 299
  • 17
    Note that `{ new(): Class }` can also be written as `new () => Class`. https://github.com/Microsoft/TypeScript/blob/v2.6.1/doc/spec.md#3.8.9 – John Leuenhagen Nov 20 '17 at 23:10
  • 31
    And note that you probably want `new (...args: any[]) => Class`. The proposed code in this answer will allow only classes whose constructor has no argument, which is probably not what you're after. – Lazar Ljubenović May 26 '18 at 08:25
  • New angular global type link https://github.com/angular/angular/blob/master/packages/core/src/interface/type.ts – wener Jan 12 '20 at 08:36
  • My class also has static methods, will they be included in the type this way? – Klesun May 01 '20 at 08:04
  • @ArturKlesun you'll have to add them to the type, i.e.: `{ new(): A; fn1(num: number): number; }` – Nitzan Tomer May 01 '20 at 19:17
  • Thanks. Or use `typeof Class` as I found out after scrolling down a bit =3 – Klesun May 01 '20 at 19:41
  • 4
    This doesn't work for classes with private constructors that only expose static factory methods (such as [LocalDate](https://js-joda.github.io/js-joda/manual/LocalDate.html)) – Gili Oct 27 '20 at 06:03
  • @Gili - as far as I recall, `private` members are not accessible outside of the declaring class, and private constructor signatures are not an exception - I am not sure a truly generic solution can be provided for `class` factory pattern implementations. Shouldn't something like this - `const createPrivate = (ctor: { init(): A }) => ctor.init();` (granted, it allows any object with `init` method returning an instance of `A`, but it is an inherent issue of structural type system used in TS). – Oleg Valter is with Ukraine Feb 03 '21 at 01:15
82

The simplest solution would be let variable: typeof Class.

Here an example:

class A {
  public static attribute = "ABC";
}

function f(Param: typeof A) {
  Param.attribute;
  new Param();
}


f(A);
MaximilianMairinger
  • 2,176
  • 2
  • 15
  • 36
  • 4
    This is the correct answer. Using the function call syntax fails when you want to access static properties, otherwise – royce3 Jan 20 '20 at 17:44
  • 4
    This approach breaks down once you start having inheritance among classes and your classes have different constructor signatures. (Think `class B extends A` with a constructor that takes more arguments than `A`'s constructor.) In such case, the compiler won't accept `f(B)`. – Louis May 01 '20 at 11:22
  • @Louis True, this was rather thought as a quick way to have a constructable with static members. Inheriting class types must be constructed via `{new: (a: T) => Instance, staticMember: string}` – MaximilianMairinger May 01 '20 at 11:29
  • 2
    Perfect. This enables typehint for static members. – Wakeel Nov 26 '20 at 01:15
  • @MaximilianMairinger why cant i use `let variable: Class`, instead of `let variable: typeof Class`. Basically why cant i use class as the type directly without `typeof` – radio_head Nov 07 '22 at 07:17
  • 1
    because `let variable: Class` means that `variable` must be *an instance of* `Class`. You have to use `typeof` if you want it to be the class itself. – Jason Kohles Nov 08 '22 at 14:33
34

Angular internally declare Type as:

export interface Type<T> extends Function { new (...args: any[]): T; }

With TypeScript3 it should be possible to add types for arguments without function overloading:

export interface TypeWithArgs<T, A extends any[]> extends Function { new(...args: A): T; } 

Example:

class A {}

function create(ctor: Type<A>): A {
    return new ctor();
}

let a = create(A);
Buggy
  • 3,539
  • 1
  • 21
  • 38
23

is it possible to hand a class to a method? And if so, does the type "any" include such class-types?

Yes and yes. any includes every type.

Here's an example of a type that includes only classes:

type Class = { new(...args: any[]): any; };

Then using it:

function myFunction(myClassParam: Class) {
}

class MyClass {}

myFunction(MyClass); // ok
myFunction({}); // error

You shouldn't have an error passing in a class for Function though because that should work fine:

var func: Function = MyClass; // ok
David Sherret
  • 101,669
  • 28
  • 188
  • 178
9

Type<T> from @angular/core is a proper interface for Class.

export interface Type<T> extends Function {
    new (...args: any[]): T;
}

You can use it to keep reference to class, instead of instance of this class:

private classRef: Type<MyCustomClass>;

or

private classRef: Type<any>;

According to background of your question with @ViewChild:

@ViewChild allows to inject "string selector" / Component / Directive

Signature of Type<any> | Function | string is an abstract signature that allows us to inject all of above.

Anton Temchenko
  • 1,440
  • 1
  • 13
  • 28
  • sadly it fails when you try `classRef : Type = (new Foo()).constructor` – l00k Dec 17 '21 at 17:25
  • @l00k constructor is a function, and unfortunately typescript doesn't know that you can create new instance using `new` operator on function in JS. Typescript expects class – Anton Temchenko Dec 20 '21 at 13:50
  • That only means constructor property is wrongly typed. Cuz at runtime class and its constructor are the same `A === A.prototype.constructor` and `(new A()).constructor === A` – l00k Dec 21 '21 at 14:05
8

Following worked for me:

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

my use case:

 interface InteractionType { [key: string]: ClassRef; }
liron_hazan
  • 1,396
  • 2
  • 19
  • 27
7

Here's an example of a type that includes only classes:

declare type Class<T = any> = new (...args: any[]) => T;
arastu
  • 432
  • 5
  • 5
2

Here is another solution without using typeof :

Typescript has a utility type called InstanceType, its declaration is :

type InstanceType<T extends abstract new (...args: any) => any> = T extends abstract new (...args: any) => infer R ? R : any

You can use it to declare your own class type with following code :

interface ClassType<InstanceType extends {} = {}> extends Function {
  new(...args: any[]): InstanceType
  prototype: InstanceType
}
LIIT
  • 496
  • 6
  • 16