Implementación de la interfaz de TypeScript con la función de firma desnuda más otros campos

Cómo escribo una clase que implementa esta interfaz de TypeScript (y mantiene feliz el comstackdor de TypeScript):

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

Vi esta respuesta relacionada: ¿Cómo hacer que una clase implemente una firma de llamada en Typescript?

Pero eso solo funciona si la interfaz solo tiene la firma de la función desnuda. No funciona si tiene miembros adicionales (como la función de texto2) para implementar.

Una clase no puede implementar todo lo que está disponible en una interfaz de mecanografía. Dos ejemplos principales son las firmas invocables y las operaciones de índice, por ejemplo: Implementar una interfaz indexable

La razón es que una interfaz está diseñada principalmente para describir todo lo que los objetos de JavaScript pueden hacer. Por lo tanto, necesita ser realmente robusto. Sin embargo, una clase de TypeScript está diseñada para representar específicamente la herencia del prototipo en una forma más OO convencional / fácil de entender / fácil de escribir.

Aún puede crear un objeto que siga esa interfaz:

 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; } ); 

Hay una forma fácil y segura de hacer esto con Object.assign de ES6:

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

Los tipos de intersección, que no creo que estuvieran disponibles en TypeScript cuando esta pregunta fue originalmente hecha y respondida, son la clave secreta para escribir bien.

Aquí hay una explicación sobre la respuesta aceptada .

Hasta donde yo sé, la única forma de implementar una firma de llamada es usar una función / método. Para implementar los miembros restantes, simplemente defínalos en esta función. Esto puede parecer extraño para los desarrolladores que provienen de C # o Java, pero creo que es normal en JavaScript.

En JavaScript, esto sería simple porque solo puede definir la función y luego agregar los miembros. Sin embargo, el sistema de tipos de TypeScript no permite esto porque, en este ejemplo, la Function no define un miembro de text2 .

Entonces, para lograr el resultado que desea, debe omitir el sistema de tipos mientras define los miembros en la función, y luego puede convertir el resultado al tipo de interfaz:

 //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 result; })(); //Invokes the closure to produce the implementation 

Tenga en cuenta que no necesita usar un cierre. Simplemente podría declarar su variable temporal en el mismo ámbito que la implementación de la interfaz resultante. Otra opción es nombrar la función de cierre para mejorar la legibilidad.

Esto es lo que creo que es un ejemplo más realista:

 interface TextRetriever { (): string; Replace(text: string); } function makeInMemoryTextRetriever(initialText: string) { var currentText = initialText; var instance: any = () => currentText; instance.Replace = (newText: string) => currentText = newText; return instance; } var inMemoryTextRetriever = makeInMemoryTextRetriever("Hello");