具有继承性的 Typescript 装饰器

标签 typescript inheritance decorator

我正在研究 Typescript 装饰器,当它们与类继承一起使用时,它们的行为似乎与我预期的完全不同。

假设我有以下代码:

class A {
    @f()
    propA;
}

class B extends A {
    @f()
    propB;
}

class C extends A {
    @f()
    propC;
}

function f() {
    return (target, key) => {
        if (!target.test) target.test = [];
        target.test.push(key);
    };
}

let b = new B();
let c = new C();

console.log(b['test'], c['test']);

哪些输出:

[ 'propA', 'propB', 'propC' ] [ 'propA', 'propB', 'propC' ]

虽然我希望这样:

[ 'propA', 'propB' ] [ 'propA', 'propC' ]

因此,target.test 似乎在 A、B 和 C 之间共享。我对这里发生的事情的理解如下:

  1. 由于 B 扩展了 A,new B() 首先触发了 A 的实例化,这触发了对 A 的 f 求值。由于 target.test 未定义,已初始化。
  2. f 然后为 B 求值,因为它扩展了 A,所以首先实例化 A。因此,此时,target.test(target 是 B)引用为 A 定义的 test。因此,我们推送 propB 在里面。在这一点上,事情按预期进行。
  3. 与步骤 2 相同,但对于 C。这一次,当 C 评估装饰器时,我希望它有一个新对象用于 test,不同于为 B 定义的对象。但是日志事实证明我错了。

任何人都可以向我解释为什么会发生这种情况 (1) 以及我将如何实现 f 以便 A 和 B 具有单独的 test 属性?

我猜你会称之为“特定于实例”的装饰器?

最佳答案

好吧,在花了几个小时玩弄和搜索网络后,我得到了一个工作版本。我不明白为什么这行得通,所以请原谅缺乏解释。

关键是使用 Object.getOwnPropertyDescriptor(target, 'test') == null 而不是 !target.test 来检查是否存在 test 属性。

如果您使用:

function f() {
    return (target, key) => {
        if (Object.getOwnPropertyDescriptor(target, 'test') == null) target.test = [];
        target.test.push(key);
    };
}

控制台会显示:

[ 'propB' ] [ 'propC' ]

这几乎就是我想要的。现在,数组特定于每个实例。但这意味着数组中缺少 'propA',因为它是在 A 中定义的。因此我们需要访问父目标并从那里获取属性。我花了一些时间才弄清楚,但您可以使用 Object.getPrototypeOf(target) 获取它。

最终的解决方案是:

function f() {
    return (target, key) => {
        if (Object.getOwnPropertyDescriptor(target, 'test') == null) target.test = [];
        target.test.push(key);

        /*
         * Since target is now specific to, append properties defined in parent.
         */
        let parentTarget = Object.getPrototypeOf(target);
        let parentData = parentTarget.test;
        if (parentData) {
            parentData.forEach(val => {
                if (target.test.find(v => v == val) == null) target.test.push(val);
            });
        }
    };
}

哪些输出

[ 'propB', 'propA' ] [ 'propC', 'propA' ]

可能有人理解为什么这有效,但上面没有启发我。

关于具有继承性的 Typescript 装饰器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43912168/

相关文章:

angular - 带有路径别名的错误自动导入

angular - 使用警告消息注销用户

java - 在 Java 中分配给这个的解决方法?

python - (python)在函数中使用装饰器进行彩色打印

javascript - PhpStorm 无法识别 webpack 别名

html - 如何根据 Angular 7中特定行的列值默认选中复选框和选择框

c++ - 成员仅因类型不同而不同的 C++ 类

c# - 从父类调用子类方法

python - 修改请求路径的 View 的自定义函数装饰器

Python:使用装饰器修改函数的内部行为