Использование декоратора для получения списка реализованных интерфейсов

Знаете ли вы, можно ли получить массив интерфейсов, реализованных классом, с помощью декоратора:

interface IWarrior {
  // ...
}

interface INinja {
  // ...
}

Итак, если я сделаю что-то вроде:

@somedecorator
class Ninja implements INinja, IWarrior {
 // ...
}

Во время выполнения Ninja будет иметь аннотацию, содержащую ["INinja", "IWarrior"] ?

Спасибо


person Remo H. Jansen    schedule 12.05.2015    source источник
comment
Интересно! Как я это читаю, однако это только параметры (а не интерфейсы и т. д.)? Кажется, что все равно будет невозможно достичь того, что вы хотите сделать, потому что интерфейсы выбрасываются. Я постараюсь изучить это дальше, как только смогу.   -  person Nypan    schedule 12.05.2015
comment
Спасибо, я не уверен, потому что эта тема не очень хорошо документирована. Я продолжил исследования и задал несколько вопросов, связанных с этим вопросом, на github.com/Microsoft/TypeScript/ Issues/3148, если вы хотите следить за ходом выполнения этой задачи.   -  person Remo H. Jansen    schedule 13.05.2015
comment
Я немного изучил это и протестировал его довольно много. См. мой обновленный ответ для объяснений и примеров.   -  person Nypan    schedule 13.05.2015
comment
Я выпустил расширенную версию компилятора TypeScript, который создает метаданные классов/интерфейсов с помощью метамодели, которую вы описали в своем последнем обновлении. Вы можете найти его здесь   -  person pcan    schedule 18.08.2016
comment
Пожалуйста, не помещайте ответы в вопрос   -  person jonrsharpe    schedule 02.11.2017


Ответы (2)


В настоящее время типы используются только во время разработки и компиляции. Информация о типе никак не транслируется в скомпилированный код JavaScript. Но вы, однако, можете передать список строк параметру декоратора следующим образом:

interface IWarrior {
  // ...
}

interface INinja {
  // ...
}


interface Function {
    interfacesList: string[];
}

@interfaces(["INinja", "IWarrior"])
class Ninja implements INinja, IWarrior {

}

function interfaces(list: string[]) {
    return (target: any) => {
        target.interfacesList = list; 
        return target;
    }
}

console.log(Ninja.interfacesList);
person Max Brodin    schedule 12.05.2015
comment
Спасибо за Ваш ответ. Хотя это решение решает мою проблему, я считаю, что можно добиться того же результата без необходимости вручную объявлять интерфейсы как строки. Компилятор должен иметь возможность генерировать эти аннотации, используя флаг --emitDecoratorMetadata. Я буду ждать больше ответов, и если это окажется невозможным, я дам вам баллы. - person Remo H. Jansen; 12.05.2015
comment
Просто для справки --emitDecoratorMetadata не работает для интерфейсов на данный момент работает только для классов и примитивов. Поскольку реализация связана с интерфейсами, мы не можем получить доступ к этой информации во время выполнения. - person Remo H. Jansen; 16.11.2016

Поскольку вся информация об интерфейсах отбрасывается во время компиляции, это невозможно. Реализация somedecorator не может получить доступ к информации, которая была отброшена компилятором.

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

Хороший вопрос о переполнении стека о реализации декораторов:

Как реализовать декоратор машинописного текста?

Редактировать:

Итак, после некоторого изучения этого вопроса ответ на ваш вопрос все еще нет. По двум причинам:

  1. Никакая информация об интерфейсах не может быть доступна после времени компиляции (с декораторами или без них).
  2. Декораторы не получают доступа к унаследованным свойствам класса.

Некоторые примеры, чтобы проиллюстрировать это:

function myDecorator() {
    // do something here.. 
}

interface INamed { name: string; }

interface ICounted { getCount() : number; }

interface ISomeOtherInterface { a: number; }

class SomeClass {
    constructor() { }
}

class Foo implements INamed {
    constructor(public name: string) { }    
}

@myDecorator
class Bar extends Foo implements ICounted {

    private _count: number;
    getCount() : number { return this._count; }

    constructor(name: string, count: number, public someProp: ISomeOtherInterface, public someClass: SomeClass) {
        super(name);
        this._count = count;
    }
}

Это приведет к скомпилированному коду (с флагом --emitDecoratorMetadata):

function myDecorator() {
    // do something here.. 
}
var SomeClass = (function () {
    function SomeClass() {
    }
    return SomeClass;
})();
var Foo = (function () {
    function Foo(name) {
        this.name = name;
    }
    return Foo;
})();
var Bar = (function (_super) {
    __extends(Bar, _super);
    function Bar(name, count, someProp, someClass) {
        _super.call(this, name);
        this.someProp = someProp;
        this.someClass = someClass;
        this._count = count;
    }
    Bar.prototype.getCount = function () { return this._count; };
    Bar = __decorate([
        myDecorator, 
        __metadata('design:paramtypes', [String, Number, Object, SomeClass])
    ], Bar);
    return Bar;
})(Foo);

Любая информация, которая будет нам доступна в декораторе (кроме самого класса), содержится в части __decorate:

__decorate([
        myDecorator, 
        __metadata('design:paramtypes', [String, Number, Object, SomeClass])
    ], Bar);

В настоящее время никакая информация о наследовании или интерфейсах не передается декораторам. Все, что делает декоратор класса, — это украшает конструктор. Это, вероятно, не изменится, особенно для интерфейсов (поскольку вся информация о них удаляется во время компиляции).

Как видно из массива типов __metadata, мы получаем информацию о типах для String, Number и класса SomeClass (аргументы конструктора). Но интерфейс ISomeOtherInterface сообщается как Object, потому что в скомпилированном javascript не сохраняется никакой информации об интерфейсах typescript. Таким образом, лучшая информация, которую мы можем получить, это Object.

Вы можете использовать что-то вроде https://github.com/rbuckton/ReflectDecorators, чтобы лучше работать с декораторами, но вы по-прежнему сможете получить доступ только к информации в __decorate и __metadata.

Итак, резюмируя. В декораторе нет информации о наследовании или интерфейсах для класса. Интерфейсы, скорее всего, никогда не будут доступны декоратору (или где-либо еще в скомпилированном коде).

person Nypan    schedule 12.05.2015
comment
Спасибо за ваш ответ и исследование, я очень ценю это. Мне жаль, что я не мог дать вам баллы, потому что я изначально обещал, что, если доступ к интерфейсам будет невозможен, я отмечу другой ответ как действительный. - person Remo H. Jansen; 14.05.2015