Passing class as parameter causes "is not newable" error

Typescript

Typescript Problem Overview


I'm trying to pass a class as a parameter to some function, that will instantiate this class and return it. Here is my code:

module A.Views {
  export class View { ... }
}

module A.App {
  export class MyApp {
    ...
    registerView(viewKlass:A.Views.View):void
    {
        var test = new viewKlass;
    } 
  }
}

When i'm trying to compile this, i'm getting:

(...): Value of type 'Views.View' is not newable.

What am I doing wrong?

If a newable type value is an object constructor how do i pass the constructor function at runtime?

Typescript Solutions


Solution 1 - Typescript

We need something to say typeof(MyClass) to distinguish objects from classes in function signatures.

Anyway, you can actually solve you problem by using constructor type signature. Considering this class:

class MyClass {
    constructor (private name: string) {
    }
}

To pass that class as a type that you can then instantiate in a function, you actually have to duplicate the class constructor signature like this:

function sample(MyClass: new (name: string) => MyClass) {
    var obj = new MyClass("hello");
}

EDIT : There is a simple solution found on codeplex:

You have to create an interface for your class like this:

interface IMyClass {
    new (name: string): MyClass;
}

Then, use it in your function signature:

function sample(MyClass: IMyClass) {
    var obj = new MyClass("hello");
}

Solution 2 - Typescript

To supplement the other answers; I have a utility type I keep around to ensure a generic parameter/field is a class/newable:

/* new T() */
export type Newable<T> = { new (...args: any[]): T; };

You can then ensure you get a class constructor:

class MyApp<TViewBase>
{
  register<TView extends TViewBase>(view: Newable<TView>) { 
  }
}

the Newable<T> approach works where typeof T --where T is a generic type-- does not.

For example: register<T extends TViewBase>(view: typeof T) results in the following error: >> [ts] 'T' only refers to a type, but is being used as a value here.

Solution 3 - Typescript

Try this:

export class MyApp {
    registerView(viewKlass: typeof A.Views.View): void {
        var test = new viewKlass();
    } 
}

Solution 4 - Typescript

There are 2 ways to pass classes in TypeScript. If you:

  • know the superclass of the class you're passing through (already recommended by Corey Alix):

     class MyClass extends MySuperClass{ }
    
     makeMyClass(classRef: typeof MySuperclass) {
         return new classRef();
     }
    
     makeMyClass(MyClass);
    
  • know the signature of the class's constructor function:

     class MyClass {
         constructor(arg: string) {}
     }
    
     makeMyClass(classRef: { new(arg: string) }) {
         return new classRef('hello');
     }
    
     makeMyClass(MyClass);
    

Solution 5 - Typescript

in case you use Angular, they have implemented Type, its similar to the Meirion Hughes answer:

import {Type} from '@angular/core';

export class MyApp {
  ...
  registerView(viewKlass: Type<A.Views.View>):void
  {
      var test = new viewKlass();
  } 
}

Solution 6 - Typescript

I know this is an old question, but here goes:

...
registerView(viewKlass: { new(): A.Views.View }):void
{
    var test = new viewKlass();
} 

Answer was found here.

Solution 7 - Typescript

Depending on your situation, the three solution bellow might not be enough:

  1. myClass: new (name: string) => MyClass
  2. interface IMyClass { new (name: string): MyClass; }; myClass: IMyClass
  3. myClass: typeof MyClass

There are cases where you would like to call some static methods like myClass.someMethod(). 1 and 2 won't allow you to do that (and pls never use any in your app) because they don't know about that method.

Or there are cases when you want to have MyClass as an abstract class and create an instance of myClass with let instance = new myClass. In that case, 3 will fail.

What I do in these cases is to create a special type for MyClass that will solve the issues I highlighted above, which looks something like this:

type MyClassContructor<T extends MyClass> = typeof MyClass & Constructor<T>;

BTW. Don't forget to add return types to your methods so you get proper intellisense.

Solution 8 - Typescript

Thank you for the gist - a slight change makes it all work fine. In the example below, we "new up" the TestView to pass in the concrete test view, rather than trying to new it within the method.

module V.Views {
   export class View {
      public someVar: any;
      // the presence of constructor doesn't affect the error triggering
   }
}

module V.App {
    export class Application {
        public registerView(url: string, viewKlass: V.Views.View): void
        {
            var test = viewKlass;
        }
    }
}

var app = new V.App.Application;

class TestView extends V.Views.View {
}

class TestView2 extends V.Views.View {
}

app.registerView('', new TestView())
app.registerView('content/view/:slug/', new TestView2())

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionzw0rkView Question on Stackoverflow
Solution 1 - TypescriptThomas LaurentView Answer on Stackoverflow
Solution 2 - TypescriptMeirion HughesView Answer on Stackoverflow
Solution 3 - TypescriptMatthew James DavisView Answer on Stackoverflow
Solution 4 - TypescriptStephen PaulView Answer on Stackoverflow
Solution 5 - Typescriptya_dimonView Answer on Stackoverflow
Solution 6 - TypescriptPerspectivusView Answer on Stackoverflow
Solution 7 - TypescriptIonel LupuView Answer on Stackoverflow
Solution 8 - TypescriptFentonView Answer on Stackoverflow