11

Do you know if it is possible to get the array of interfaces implemented by a class using a decorator:

interface IWarrior {
  // ...
}

interface INinja {
  // ...
}

So If I do something like:

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

At run-time Ninja will have an annotation which contains ["INinja", "IWarrior"] ?

Thanks

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
Remo H. Jansen
  • 23,172
  • 11
  • 70
  • 93
  • 1
    Interesting! The way I am reading this however it is only parameters (not interfaces etc)? It seems it will still be impossible to achieve what you want to do because interfaces gets thrown away. I will try to look in to this further my self as soon as I can. – Nypan May 12 '15 at 14:21
  • Thanks, I'm not sure because this topic is not really well documented. I've continued researching and asked some questions related with this question at https://github.com/Microsoft/TypeScript/issues/3148 in case you want to follow the progress in this. – Remo H. Jansen May 13 '15 at 10:52
  • I looked in to this a bit and tested it quite a lot. Se my updated answer for explanations and examples. – Nypan May 13 '15 at 17:39
  • 1
    I released an enhanced version of the TypeScript compiler that produces classes/interfaces metadata with the metamodel you described in your last update. You can find it [here](https://github.com/pcan/reflec-ts) – pcan Aug 18 '16 at 12:39
  • Please don't put answers in the question – jonrsharpe Nov 02 '17 at 18:15

2 Answers2

7

Currently, types are used only during development and compile time. The type information is not translated in any way to the compiled JavaScript code. But you however can pass the list of strings to the decorator parameter like this:

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);
Max Brodin
  • 3,903
  • 1
  • 14
  • 23
  • Thanks for your answer. While this solution solves my problem I believe that it may be possible to achieve the same result without having to manually declare the interfaces as strings. The compiler should be able to generate these annotations using the --emitDecoratorMetadata flag. I will wait for more answers and if it turns to be not possible I will give you the points. – Remo H. Jansen May 12 '15 at 13:15
  • Just for reference --emitDecoratorMetadata does not work for interfaces at the moment only works for classes and primitives. Because implementing is related with interfaces we cannot access that information at run-time. – Remo H. Jansen Nov 16 '16 at 12:11
3

Since all information about interfaces is thrown away at compile time this will not be possible. There is no way for the implementation of the somedecorator to access information that has been thrown away by the compiler.

Passing the interface names to the decorator as strings is possible, this is however not that useful since all information provided by the interfaces will be gone at run time.

A good stack overflow question about implementing decorators:

How to implement a typescript decorator?

Edit:

So after researching this for a while the answer to your question is still no. For two reasons:

  1. No information about interfaces can be accessed after compile time (with or without decorators)
  2. Decorators do not get access to the inherited properties of a class.

Som examples to illustrate this:

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;
    }
}

This will result in compiled code (with the --emitDecoratorMetadata flag):

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);

Any information that will be available to us in the decorator (except for the class it self) is contained in the __decorate part:

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

As it stands now no information about inheritance or interfaces are passed along to the decorators. All a decorator for a class does is decorate the constructor. This will probably not change, certainly not for the interfaces (since all information about them gets thrown away at compile time).

As we can se in the type array of the __metadata we get the type information for String, Number and the class SomeClass (constructor arguments). But the interface ISomeOtherInterface is reported as Object, this is because no information about typescript interfaces is retained in the compiled javascript. So the best information we can get is Object.

You can use something like https://github.com/rbuckton/ReflectDecorators to better work with decorators but you will still only be able to access the information in __decorate and __metadata.

So to summarise. No information about inheritance or interfaces for a class is available in a decorator. Interfaces will likely never be available to a decorator (or anywhere else in the compiled code).

Community
  • 1
  • 1
Nypan
  • 6,980
  • 3
  • 21
  • 28
  • Thanks for your answer and research I really appreciate it. I'm sorry I could not give you de points because I originally promised that if it was not possible to access the interfaces I would flag the other answer as valid. – Remo H. Jansen May 13 '15 at 22:53