Реализация интерфейса TypeScript с подписью голой функции плюс другие поля

Как мне написать класс, который реализует этот интерфейс TypeScript (и поддерживает компилятор TypeScript счастливым):

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

Я видел этот связанный ответ: Как заставить класс реализовать сигнатуру вызова в Typescript?

Но это работает, только если интерфейс имеет только подпись функции. Это не работает, если у вас есть дополнительные члены (например, функция text2) для реализации.

3 ответа

Решение

Класс не может реализовать все, что доступно в интерфейсе машинописи. Два основных примера - вызываемые сигнатуры и операции с индексами, например: Реализация индексируемого интерфейса.

Причина в том, что интерфейс в первую очередь предназначен для описания всего, что могут делать объекты JavaScript. Поэтому он должен быть действительно надежным. Класс TypeScript, тем не менее, предназначен для специфического представления наследования прототипа более общепринятым / простым для понимания / простым для ввода способом.

Вы все еще можете создать объект, который следует этому интерфейсу:

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

Есть простой и безопасный способ сделать это с ES6 Object.assign:

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

Типы пересечений, которые, я не думаю, были доступны в TypeScript, когда этот вопрос первоначально задавался и отвечали, являются секретным соусом для правильного набора текста.

Вот уточнение принятого ответа.

Насколько я знаю, единственный способ реализовать вызов-подпись - это использовать функцию / метод. Чтобы реализовать остальные члены, просто определите их в этой функции. Это может показаться странным для разработчиков из C# или Java, но я думаю, что это нормально в JavaScript.

В JavaScript это было бы просто, потому что вы можете просто определить функцию, а затем добавить членов. Однако система типов TypeScript не позволяет этого, потому что в этом примере Function не определяет text2 член.

Таким образом, чтобы достичь желаемого результата, вам нужно обойти систему типов, пока вы определяете члены функции, а затем вы можете привести результат к типу интерфейса:

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

Обратите внимание, что вам не нужно использовать закрытие. Вы можете просто объявить вашу временную переменную в той же области видимости, что и полученная реализация интерфейса. Другой вариант - назвать функцию закрытия для улучшения читабельности.

Вот то, что я считаю более реалистичным примером:

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");
Другие вопросы по тегам