Как заставить класс реализовать подпись вызова в Typescript?

Я определил следующий интерфейс в машинописи:

interface MyInterface {
    () : string;
}

Этот интерфейс просто вводит сигнатуру вызова, которая не принимает параметров и возвращает строку. Как мне реализовать этот тип в классе? Я пробовал следующее:

class MyType implements MyInterface {
    function () : string {
        return "Hello World.";
    }
}

Компилятор постоянно говорит мне, что

Класс "MyType" объявляет интерфейс "MyInterface", но не реализует его: для типа "MyInterface" требуется подпись вызова, а для типа "MyType" ее нет.

Как я могу реализовать подпись вызова?

3 ответа

Решение

Классы не могут соответствовать этому интерфейсу. Я думаю, что ближе всего можно получить этот класс, который будет генерировать код, функционально соответствующий интерфейсу (но не в соответствии с компилятором).

class MyType implements MyInterface {
  constructor {
    return "Hello";
  }
}
alert(MyType());

Это создаст рабочий код, но компилятор будет жаловаться, что MyType не вызывается, потому что имеет подпись new() = 'string' (даже если вы называете это с new, он вернет объект).

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

var MyType = (() : MyInterface => {
  return function() { 
    return "Hello"; 
  }
})();
alert(MyType());

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

interface Greeter {
    (): void;
    setName(name: string): void;
}

class ConsoleGreeter {

    private constructor( // constructable via `create()`
        private name = 'world'
    ) {}

    public call(): void {
        console.log(`Hello ${this.name}!`);
    }

    public setName(name: string) {
        this.name = name;
    }

    public static create(): Greeter {
        const instance = new ConsoleGreeter();
        return Object.assign(
            () => instance.call(),
            {
                setName: (name: string) => instance.setName(name)
                // ... forward other methods
            }
        );
    }
}

const greeter = ConsoleGreeter.create();
greeter.setName('Dolly');
greeter(); // prints 'Hello Dolly!'

Даунсайд: greeter instanceof ConsoleGreeter является false

Примеры кода в этом ответе предполагают следующее объявление:

var implementation: MyInterface;

Обеспечение реализации вызываемого интерфейса

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

Например:

implementation = () => "Hello";

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

implementation = <MyInterface>() => "Hello";

Предоставление многоразовой реализации

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

Например:

function Greet() {
    return "Hello";
}

implementation = Greet;

Предоставление параметризованной реализации

Возможно, вы захотите иметь возможность параметризовать реализацию так же, как вы можете параметризовать класс. Вот один из способов сделать это:

function MakeGreeter(greeting: string) {
    return () => greeting;
}

implementation = MakeGreeter("Hello");

Если вы хотите, чтобы результат был напечатан как интерфейс, просто явно установите тип возвращаемого значения или приведите возвращаемое значение.

Другие вопросы по тегам