Passing class as parameter causes "is not newable" error
TypescriptTypescript 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:
myClass: new (name: string) => MyClass
interface IMyClass { new (name: string): MyClass; }; myClass: IMyClass
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())