web-dev-qa-db-fra.com

Implémentation d'une interface TypeScript avec une signature de fonction nue et d'autres champs

Comment puis-je écrire une classe qui implémente cette interface TypeScript (et maintient le compilateur TypeScript heureux):

interface MyInterface {
    (): string;
    text2(content: string);
}

J'ai vu cette réponse connexe: Comment faire pour qu'une classe implémente une signature d'appel dans Typescript?

Mais cela ne fonctionne que si l'interface n'a que la signature de fonction nue. Cela ne fonctionne pas si vous avez des membres supplémentaires (comme la fonction text2) à implémenter.

44
user1147862

Une classe ne peut pas implémenter tout ce qui est disponible dans une interface TypeScript. Deux exemples principaux sont les signatures appelables et les opérations d'index, par exemple : Implémenter une interface indexable

La raison en est qu'une interface est principalement conçue pour décrire tout ce que les objets JavaScript peuvent faire. Par conséquent, il doit être vraiment robuste. Une classe TypeScript est cependant conçue pour représenter spécifiquement l'héritage du prototype d'une manière plus OO conventionnelle/facile à comprendre/facile à taper).

Vous pouvez toujours créer un objet qui suit cette interface:

interface MyInterface {
    (): string;
    text2(content: string);
}

var MyType = ((): MyInterface=>{
  var x:any = function():string { // Notice the any 
      return "Some string"; // Dummy implementation 
  }
  x.text2 = function(content:string){
      console.log(content); // Dummy implementation 
  }
  return x;
}
);
43
basarat

Il existe un moyen simple et sûr de le faire avec l'ES6 Object.assign:

const foo: MyInterface = Object.assign(
  // Callable signature implementation
  () => 'hi',
  {
    // Additional properties
    text2(content) { /* ... */ }
  }
)

Les types d'intersection, qui, je pense, n'étaient pas disponibles dans TypeScript lorsque cette question a été posée et répondue à l'origine, sont la solution secrète pour obtenir le bon typage.

16
Tom Crockett

Voici une élaboration sur la réponse acceptée .

Pour autant que je sache, la seule façon d'implémenter une signature d'appel est d'utiliser une fonction/méthode. Pour implémenter les membres restants, il suffit de les définir sur cette fonction. Cela peut sembler étrange aux développeurs venant de C # ou de Java, mais je pense que c'est normal en JavaScript.

En JavaScript, cela serait simple car vous pouvez simplement définir la fonction puis ajouter les membres. Cependant, le système de types de TypeScript ne permet pas cela car, dans cet exemple, Function ne définit pas un text2 membre.

Ainsi, pour obtenir le résultat souhaité, vous devez contourner le système de types pendant que vous définissez les membres sur la fonction, puis vous pouvez convertir le résultat en type d'interface:

//A closure is used here to encapsulate the temporary untyped variable, "result".
var implementation = (() => {
    //"any" type specified to bypass type system for next statement.
    //Defines the implementation of the call signature.
    var result: any = () => "Hello";

    //Defines the implementation of the other member.
    result.text2 = (content: string) => { };

    //Converts the temporary variable to the interface type.
    return <MyInterface>result;
})(); //Invokes the closure to produce the implementation

Notez que vous n'avez pas besoin d'utiliser une fermeture. Vous pouvez simplement déclarer votre variable temporaire dans la même portée que l'implémentation d'interface résultante. Une autre option consiste à nommer la fonction de fermeture pour améliorer la lisibilité.

Voici ce que je pense être un exemple plus réaliste:

interface TextRetriever {
    (): string;
    Replace(text: string);
}

function makeInMemoryTextRetriever(initialText: string) {
    var currentText = initialText;
    var instance: any = () => currentText;
    instance.Replace = (newText: string) => currentText = newText;

    return <TextRetriever>instance;
}

var inMemoryTextRetriever = makeInMemoryTextRetriever("Hello");
9
Sam