Как работают интерфейсы с конструктивными сигнатурами?

У меня возникают проблемы с тем, как работают конструкторы в интерфейсах. Возможно, я совершенно ничего не понимаю. Но я искал ответы на какое-то время, и я не могу найти ничего, что связано с этим.

Как реализовать следующий интерфейс в классе TypeScript:

interface MyInterface {
    new ( ... ) : MyInterface;
}

Андерс Хейлсберг создает интерфейс, содержащий нечто подобное этому видео (примерно 14 минут). Но для жизни меня я не могу реализовать это в классе.

Я, наверное, что-то недопонимаю, чего я не получу?

ИЗМЕНИТЬ:

Прояснить. С "новым (...)" я имел в виду "что угодно". Моя проблема в том, что я не могу получить даже самую базовую версию этой работы:

interface MyInterface {
    new () : MyInterface;
}

class test implements MyInterface {
    constructor () { }
}

Это не компиляция для меня. Я получаю "Тест класса", объявляющий интерфейс "MyInterface", но не реализующий его. Тип "MyInterface" требует наличия сигнатуры конструкции, но при попытке его скомпилировать тип "test" отсутствует ".

ИЗМЕНИТЬ:

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

interface MyInterface {
    new () : MyInterface;
}

class test implements MyInterface {
    constructor () => test { return this; }
}

Недействительно TypeScript, и это не решает проблему. Вы не можете определить возвращаемый тип конструктора. Он вернет "тест". Подпись:   class test {       constructor() {}   } Кажется, это "новый() = > тест" (полученный путем зависания над "классом" в онлайн-редакторе с помощью только того кода, который вставлен). И это то, что мы хотели бы и что я думал, что это будет.

Может ли кто-нибудь представить пример этого или чего-то подобного там, где он собственно компилируется?

EDIT (снова...):

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

var MyClass = (function () {
    function MyClass() { }
    return MyClass;
})();

interface MyInterface {
    new () : MyInterface;
}

var testFunction = (foo: MyInterface) : void =>  { }
var bar = new MyClass();
testFunction(bar);

Итак, это только функция TypeScript, которая позволяет вам взаимодействовать с javascript? Или можно реализовать его в TypeScript без необходимости реализации класса с помощью javascript?

Ответ 1

Построение подписей в интерфейсах не реализуется в классах; они предназначены только для определения существующих JS API, которые определяют "новую" функцию. Вот пример, содержащий интерфейсы new, которые работают:

interface ComesFromString {
    name: string;
}

interface StringConstructable {
    new(n: string): ComesFromString;
}

class MadeFromString implements ComesFromString {
    constructor (public name: string) {
        console.log('ctor invoked');
    }
}

function makeObj(n: StringConstructable) {
    return new n('hello!');
}

console.log(makeObj(MadeFromString).name);

Это создает фактическое ограничение для того, что вы можете вызвать makeObj с помощью:

class Other implements ComesFromString {
    constructor (public name: string, count: number) {
    }
}

makeObj(Other); // Error! Other constructor doesn't match StringConstructable

Ответ 2

В моем поиске точно такого же вопроса я пошел посмотреть, как это сделал TypeScript -Team...

Они объявляют интерфейс, а затем переменную с именем, соответствующим точному имени интерфейса. Это также способ ввода статических функций.

Пример из lib.d.ts:

interface Object {
    toString(): string;
    toLocaleString(): string;
    // ... rest ...
}
declare var Object: {
    new (value?: any): Object;
    (): any;
    (value: any): any;
    // ... rest ...
}

Я пробовал это, и он работает как шарм.

Ответ 3

Ну, интерфейс с сигнатурой конструкции не предназначен для реализации каким-либо классом (на первый взгляд это может показаться странным для парней с С#/Java-опытом, таких как я, но дать ему шанс). Это немного отличается.

На мгновение представьте, что это интерфейс с сигнатурой вызова (например, @FunctionalInterface в мире Java). Его цель - описать тип функции. Предполагается, что описанная сигнатура удовлетворяется функциональным объектом... а не какой-либо высокоуровневой функцией или методом. Это должна быть функция, которая знает, как создать объект, функция, которая вызывается при использовании ключевого слова new.

Таким образом, интерфейс с сигнатурой конструкции определяет сигнатуру конструктора! Конструктор вашего класса, который должен соответствовать сигнатуре, определенной в интерфейсе (представьте, что конструктор реализует интерфейс). Это как фабрика!

Вот небольшой фрагмент кода, который пытается продемонстрировать наиболее распространенное использование:

interface ClassicInterface { // old school interface like in C#/Java
    method1();
    ...
    methodN();
}

interface Factory { //knows how to construct an object
    // NOTE: pay attention to the return type
    new (myNumberParam: number, myStringParam: string): ClassicInterface
}

class MyImplementation implements ClassicInterface {
    // The constructor looks like the signature described in Factory
    constructor(num: number, s: string) { } // obviously returns an instance of ClassicInterface
    method1() {}
    ...
    methodN() {}
}

class MyOtherImplementation implements ClassicInterface {
    // The constructor looks like the signature described in Factory
    constructor(n: number, s: string) { } // obviously returns an instance of ClassicInterface
    method1() {}
    ...
    methodN() {}
}

// And here is the polymorphism of construction
function instantiateClassicInterface(ctor: Factory, myNumberParam: number, myStringParam: string): ClassicInterface {
    return new ctor(myNumberParam, myStringParam);
}

// And this is how we do it
let iWantTheFirstImpl = instantiateClassicInterface(MyImplementation, 3.14, "smile");
let iWantTheSecondImpl = instantiateClassicInterface(MyOtherImplementation, 42, "vafli");

Ответ 4

С точки зрения дизайна обычно не требуется указывать требования к конструктору в интерфейсе. Интерфейс должен описывать операции, которые вы можете выполнять на объекте. Для разных классов, реализующих интерфейс, должно быть разрешено требовать разные параметры конструктора, если им нужно.

Например, если у меня был интерфейс:

interface ISimplePersistence {
    load(id: number) : string;
    save(id: number, data: string): void;
}

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

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

Интерфейсы с сигнатурами конструкций не проверяются типом

Ответ 5

Чтобы достичь намеченного поведения, вы можете использовать Decorators, хотя это, вероятно, не то, для чего они должны использоваться.

Это

interface MyInterface {
    new ();
}

function MyInterfaceDecorator(constructor: MyInterface) {
}


@MyInterfaceDecorator
class TestClass {
    constructor () { }
}

компилируется без проблем. Напротив, следующее определение для TestClass

// error TS2345: Argument of type 'typeof TestClass' is not assignable to parameter of type 'MyInterface'.
@MyInterfaceDecorator
class TestClass {
    constructor (arg: string) { }
}

не будет компилироваться.