93 votes

Le passage d'une classe en tant que paramètre provoque l'erreur "is not newable".

J'essaie de passer une classe comme paramètre à une fonction qui instancie cette classe et la renvoie. Voici mon code :

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

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

Quand j'essaye de compiler ceci, j'obtiens :

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

Qu'est-ce que je fais de mal ?

Si une valeur de type newable est un constructeur d'objet, comment puis-je passer la fonction du constructeur au moment de l'exécution ?

124voto

Thomas Laurent Points 1120

Nous avons besoin de quelque chose qui dise typeof(MyClass) pour distinguer les objets des classes dans les signatures de fonctions.

Quoi qu'il en soit, vous pouvez résoudre votre problème en utilisant la signature du type de constructeur. En considérant cette classe :

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

Pour transmettre cette classe comme un type que vous pouvez ensuite instancier dans une fonction, vous devez en fait dupliquer la signature du constructeur de la classe comme ceci :

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

EDIT : Il existe une solution simple trouvée sur codeplex :

Vous devez créer une interface pour votre classe comme ceci :

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

Ensuite, utilisez-le dans votre signature de fonction :

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

40voto

Meirion Hughes Points 1855

Pour compléter les autres réponses, j'ai un type utilitaire que je garde à portée de main pour m'assurer qu'un paramètre/champ générique est une classe/newable :

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

Vous pouvez alors vous assurer que vous obtenez un constructeur de classe :

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

le site Newable<T> L'approche fonctionne lorsque typeof T --où T est un type générique - ne le fait pas.

Par exemple : register<T extends TViewBase>(view: typeof T) donne lieu à l'erreur suivante :

[ts] "T" ne fait référence qu'à un type, mais il est utilisé ici comme une valeur.

32voto

Essayez ça :

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

25voto

Steve Paul Points 135

Il existe deux façons de transmettre des classes en TypeScript. Si vous :

  • connaître la superclasse de la classe par laquelle on passe (déjà recommandé par Corey Alix) :

    class MyClass extends MySuperClass{ }
    
    makeMyClass(classRef: typeof MySuperclass) {
        return new classRef();
    }
    
    makeMyClass(MyClass);
  • connaître la signature de la fonction constructeur de la classe :

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

14voto

ya_dimon Points 26

Si vous utilisez Angular, ils ont mis en œuvre Type il est similaire au Réponse de Meirion Hughes :

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

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X