Реализация интерфейса 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");