Implementing TypeScript interface with bare function signature plus other fields Implementing TypeScript interface with bare function signature plus other fields typescript typescript

Implementing TypeScript interface with bare function signature plus other fields


A class cannot implement everything that is available in a typescript interface. Two prime examples are callable signatures and index operations e.g. : Implement an indexible interface

The reason is that an interface is primarily designed to describe anything that JavaScript objects can do. Therefore it needs to be really robust. A TypeScript class however is designed to represent specifically the prototype inheritance in a more OO conventional / easy to understand / easy to type way.

You can still create an object that follows that 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;});


There's an easy and type-safe way to do this with ES6's Object.assign:

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

Intersection types, which I don't think were available in TypeScript when this question was originally asked and answered, are the secret sauce to getting the typing right.


Here's an elaboration on the accepted answer.

As far as I know, the only way to implement a call-signature is to use a function/method. To implement the remaining members, just define them on this function. This might seem strange to developers coming from C# or Java, but I think it's normal in JavaScript.

In JavaScript, this would be simple because you can just define the function and then add the members. However, TypeScript's type system doesn't allow this because, in this example, Function doesn't define a text2 member.

So to achieve the result you want, you need to bypass the type system while you define the members on the function, and then you can cast the result to the interface type:

//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

Note that you don't need to use a closure. You could just declare your temporary variable in the same scope as the resulting interface implementation. Another option is to name the closure function to improve readability.

Here's what I think is a more realistic example:

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