【问题标题】:Get property class instance in property decorator在属性装饰器中获取属性类实例
【发布时间】:2019-02-01 11:50:35
【问题描述】:

我正在尝试编写一个@Prop 装饰器来帮助我设置自定义元素属性。

这是我想要实现的代码:

class MyClass extends HtmlElement {
   get text() {
     return this.getAttribute('text')
   }

   set text(newVal){
     this.setAttribute('text', newVal)
   }

   connectedCallback() {
     this.innerHTML = `<div>${this.text}</div>`
   }
}

这是带有装饰器的类

class MyClass extends HtmlElement {
   @Prop() text: string;

   connectedCallback() {
     this.innerHTML = `<div>${this.text}</div>`
   }
}

这是装饰器函数

const Prop = () => (target : any, key : string) => {
    const getter = () => target.getAttribute(key);
    const setter = (newVal) => target.setAttribute(key, newVal);

    if (!delete this[key]) return;
    Object.defineProperty(target, key, {
        get: getter,
        set: setter
    });
}

但是,每当调用 getter 函数时,我都会收到此错误:

Uncaught TypeError: Illegal invocation
    at HTMLElement.getter (app.js:16)

查看app.js:16 会发现这行代码:

const getter = () => target.getAttribute(key);

target.getAttribute(key); 带有下划线。

【问题讨论】:

    标签: typescript typescript-decorator


    【解决方案1】:

    要定义 getter 和 setter,不要只是删除属性然后重新添加它,而是使用属性描述符

    const Prop = () => (target : any, key : string, descriptor: PropertyDescriptor) => {
      // Could be inlined...
      const getter = () => target.getAttribute(key);
      const setter = (newVal) => target.setAttribute(key, newVal);
    
      descriptor.set = setter
      descriptor.get = getter
    }
    

    https://www.typescriptlang.org/docs/handbook/decorators.html

    【讨论】:

    • 描述符参数是为属性装饰器传递的还是只是方法装饰器?
    • 是的。这是否解决了您的问题,还是更多地与:target.getAttribute(key)
    • target.getAttribute(key)有关。 target 似乎只是类的原型,而不是实例。有没有办法获取实例?
    • 我明白了...您确定目标确实有 getAttribute 方法吗?您可以尝试记录并确认它吗?因为我怀疑 getter 的 this 上下文在某处发生了变化
    • 如果我尝试console.log(target.getAttribute(key)),我会收到Illegal invocation 错误。如果我只是 console.log(target) 并查看 attributes 属性,则数组中会出现相同的错误。 Exception: TypeError: Illegal invocation at HTMLElement.remoteFunction (&lt;anonymous&gt;:2:14)
    【解决方案2】:

    装饰器以类作为目标而不是类的实例来调用。该类的实例将以this 的形式出现在getter /setter 函数中。这就是为什么在这里使用箭头函数不是一个好主意,因为它们从声明站点捕获this。在这种情况下,使用常规函数效果最好。

    const Prop = () => (target: any, key: string, descriptor: PropertyDescriptor) => {
        const getter = function (this: HTMLElement) {
            return this.getAttribute(key);
        }
        const setter = function (this: HTMLElement, newVal) {
            this.setAttribute(key, newVal);
        }
        descriptor = descriptor || {};
        descriptor.get = getter;
        descriptor.set = setter;
        return descriptor;
    }
    
    class MyClass extends HTMLElement {
        @Prop() text: string;
    
        connectedCallback() {
            this.innerHTML = `<div>${this.text}</div>`
        }
    }
    

    【讨论】:

    • 谢谢!对于类属性,PropertyDescriptor 未传入,因此必须使用 Object.defineProperty 进行修改
    • @Rodrigo 虽然没有传入,但如果它未定义,我会创建它,如果返回描述符,装饰器机器将调用defineProperty。您应该这样做以允许装饰器的组合(每个装饰器修改定义并注册最终结果,您的定义也可能被覆盖)。您应该尝试代码,按照发布的方式工作,我总是测试 ;-)
    • 当我尝试使用存在的第三个参数进行编译时,TypeScript 会抛出这个错误Unable to resolve signature of property decorator when called as an expression.
    猜你喜欢
    • 2018-04-28
    • 1970-01-01
    • 1970-01-01
    • 2020-06-12
    • 1970-01-01
    • 2020-06-20
    • 2013-08-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多