【问题标题】:Using a Decorator to get list of implemented interfaces使用装饰器获取已实现接口的列表
【发布时间】:2015-07-23 04:19:42
【问题描述】:

你知道是否可以使用装饰器获取类实现的接口数组:

interface IWarrior {
  // ...
}

interface INinja {
  // ...
}

所以如果我这样做:

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

在运行时 Ninja 会有一个包含 ["INinja", "IWarrior"] 的注解?

谢谢

【问题讨论】:

  • 有趣!我读这个的方式但是它只是参数(不是接口等)?似乎仍然无法实现您想要做的事情,因为接口被丢弃了。我会尽快尝试进一步了解我自己。
  • 谢谢,我不确定,因为这个主题并没有很好的记录。我一直在继续研究,并在github.com/Microsoft/TypeScript/issues/3148 提出了一些与此问题相关的问题,以防您想关注这方面的进展。
  • 我对此进行了一些研究并对其进行了很多测试。请参阅我的最新答案以获取解释和示例。
  • 我发布了 TypeScript 编译器的增强版本,它使用您在上次更新中描述的元模型生成类/接口元数据。你可以找到它here
  • 请不要在问题中填写答案

标签: typescript aurelia babeljs typescript1.5 ecmascript-2016


【解决方案1】:

由于所有关于接口的信息都在编译时被丢弃,这是不可能的。 somedecorator 的实现无法访问被编译器丢弃的信息。

将接口名称作为字符串传递给装饰器是可能的,但这并不是很有用,因为接口提供的所有信息都将在运行时消失。

关于实现装饰器的一个很好的堆栈溢出问题:

How to implement a typescript decorator?

编辑:

因此,在研究了一段时间后,您的问题的答案仍然是否定的。有两个原因:

  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 中的信息。

总结一下。装饰器中没有关于类的继承或接口的信息。装饰器(或编译代码中的其他任何地方)可能永远无法使用接口。

【讨论】:

  • 感谢您的回答和研究,非常感谢。很抱歉,我不能给你分数,因为我最初承诺如果无法访问接口,我会将另一个答案标记为有效。
【解决方案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);

【讨论】:

  • 感谢您的回答。虽然此解决方案解决了我的问题,但我相信无需手动将接口声明为字符串即可获得相同的结果。编译器应该能够使用 --emitDecoratorMetadata 标志生成这些注释。我会等待更多的答案,如果不可能,我会给你积分。
  • 仅供参考 --emitDecoratorMetadata 目前不适用于接口,仅适用于类和原语。因为实现与接口相关,我们无法在运行时访问该信息。
猜你喜欢
  • 1970-01-01
  • 2019-07-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-02
  • 2014-08-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多