Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a Decorator to get list of implemented interfaces

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

like image 558
Remo H. Jansen Avatar asked May 12 '15 10:05

Remo H. Jansen


People also ask

How can I make a decorator depend on an interface?

the decorator should depend on an instance of the interface it implements. This allows decorators to be applied to other decorators or implementations and makes the decorator easily testable So how can we achieve all this? Have the decorator explicitly depend on a concrete implementation of the wrapped class.

Should decorator explicitly depend on a concrete implementation of the wrapped class?

Have the decorator explicitly depend on a concrete implementation of the wrapped class. ❌ Requires the decorated implementation to be registered against it’s concrete type - not the interface Overall, it’s not great.

What is a decorated class in Java?

A decorated class is a base class of concrete decorator classes and it inherits from the base component class. So the decorators can be used in the place of concrete components. Let's start learning the Decorator Pattern with one real time problem.

How to use Decorators with classes in Python?

You can use decorators with classes as well. Let's see how you use decorators with a Python class. In this example, notice there is no @ character involved. With the __call__ method the decorator is executed when an instance of the class is created. This class keeps track of the number of times a function to query to an API has been run.


2 Answers

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);
like image 169
Max Brodin Avatar answered Sep 19 '22 14:09

Max Brodin


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

like image 41
Nypan Avatar answered Sep 19 '22 14:09

Nypan