132

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.

0

9 Answers 9

115

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

Sign up to request clarification or add additional context in comments.

9 Comments

Note that { new(): Class } can also be written as new () => Class. github.com/Microsoft/TypeScript/blob/v2.6.1/doc/spec.md#3.8.9
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.
My class also has static methods, will they be included in the type this way?
This doesn't work for classes with private constructors that only expose static factory methods (such as LocalDate)
|
95
+50

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);

6 Comments

This is the correct answer. Using the function call syntax fails when you want to access static properties, otherwise
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 True, this was rather thought as a quick way to have a constructable with static members. Inheriting class types must be constructed via {new<T>: (a: T) => Instance<T>, staticMember: string}
Perfect. This enables typehint for static members.
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.
|
37

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);

Comments

25

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

Comments

10

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.

3 Comments

sadly it fails when you try classRef : Type<Foo> = (new Foo()).constructor
@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
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
8

Following worked for me:

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

my use case:

 interface InteractionType { [key: string]: ClassRef; }

1 Comment

sadly it fails when you try classRef : ClassRef<Foo> = (new Foo()).constructor
7

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

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

Comments

3

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
}

Comments

3

For those going nuts out there, try one of these types:

export type Constructor<T = any> = new (...args: any[]) => T;
export type Class<T = any> = InstanceType<Constructor<T>>;

Ref: https://stackoverflow.com/a/70368495

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.