typescriptconstructorinstancetypescript-typings

Difference in TypeScript between types `InstanceType<typeof MyClass>` and `: MyClass`


I've been trying to understand if there is any difference between using InstanceType to assign a type, or simply using the Class name.

In particular, given the class:

MyClass {

  public static foo: string = 'abc'

  public makeFoo() {
    // awesome maker
  }

}

When I want to use an instance of the class, it seems that there is no difference between:

// inside some other Class
private myClassInstance: InstanceType<typeof MyClass> 
...
this.myClassInstance = new MyClass()

and

// inside some other Class
private myClassInstance: MyClass 
...
this.myClassInstance = new MyClass()

At least in VSCode I don't get any visible difference in using any of the two. However I guess a difference must exist, if TS has implemented InstanceType<>.

Anyone knows if there is a difference?


Solution

  • Anyone knows if there is a difference?

    No, there is no difference. But you have to use InstanceType in some cases.


    The InstanceType helper type exists because a class name can represent two things:

    In TypeScript, when you declare a class, you declare both of them: the constructor function (thus its type) and the type of the generated instances (class fields and methods).

    In your example, you are declaring:

    As you didn't specify a constructor function, its type is new () => MyClass and you can extract the type MyClass from it thanks to InstanceType.


    As a real-world example, imagine you are trying to implement a factory (function that returns instances of classes). One naive implementation would look like this:

    declare function factory<T>(ctor: T): T;
    
    class A { }
    
    const res = factory(A);
    //    ^ this gives `res: typeof A`, which is NOT an instance type.
    

    While TypeScript doesn't give an error, you are telling TypeScript to treat res as the constructor function of A instead of the instance of A.

    However, this implementation works:

    type Constructor = new (...args: any[]) => any;
        
    declare function factory<T extends Constructor>(ctor: T): InstanceType<T>;
    
    class A { }
    
    const res = factory(A);
    //    ^ this gives `res: A`, as expected.
    

    Because InstanceType extracted the return type.

    Playground