Внедрение интерфейса TypeScript с сигнатурой голой функции и другими полями
Как написать класс, который реализует этот интерфейс TypeScript (и поддерживает компилятор TypeScript):
interface MyInterface {
(): string;
text2(content: string);
}
Я видел этот ответ:
Как заставить класс реализовать подпись вызова в Typescript?
Но это работает только в том случае, если интерфейс имеет только голой функции. Это не работает, если у вас есть дополнительные члены (такие как функция text2), которые будут реализованы.
Ответы
Ответ 1
Класс не может реализовать все, что доступно в интерфейсе typescript. Два простых примера являются вызываемыми сигнатурами и индексами, например.: Внедрить индексируемый интерфейс
Причина в том, что интерфейс в первую очередь предназначен для описания всего, что могут сделать объекты JavaScript. Поэтому он должен быть действительно надежным. Класс typescript, однако, предназначен для того, чтобы представлять специфическое наследование прототипа в более обычном/легко понятном или легком в использовании способе OO.
Вы все равно можете создать объект, следующий за этим интерфейсом:
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;
}
);
Ответ 2
Здесь вы найдете описание принятого ответа.
Насколько я знаю, единственный способ реализовать сигнатуру вызова - использовать функцию/метод. Чтобы реализовать оставшиеся элементы, просто определите их для этой функции. Это может показаться странным разработчикам из С# или 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");
Ответ 3
Там простой и безопасный способ сделать это с ES6 Object.assign
:
const foo: MyInterface = Object.assign(
// Callable signature implementation
() => 'hi',
{
// Additional properties
text2(content) { /* ... */ }
}
)
Типы пересечений, которые, как я думаю, не были доступны в TypeScript, когда этот вопрос изначально был задан и ответил, являются секретным соусом для правильного ввода текста.