如何让我的类装饰器只在继承链中的最外层类上运行?

Posted

技术标签:

【中文标题】如何让我的类装饰器只在继承链中的最外层类上运行?【英文标题】:How can I make my class decorator only run on the outermost class in an inheritance chain? 【发布时间】:2019-03-28 05:39:40 【问题描述】:

我正在使用class-validator 装饰器来验证涉及在运行时来自我的应用程序外部的数据的数据类。我希望我的类在实例化时验证自己。我编写了一个类装饰器来轻松地将这个功能添加到类中。

import * as t from 'class-validator';

interface Class 
  new(...args: any[]): ;


export function autoValidate<T extends Class>(target: T) 
  return class extends target 
    constructor(...args: any[]) 
      super(...args);

      const errors = t.validateSync(this);

      if (errors.length > 0) 
        throw errors;
      
    
  ;

这个类装饰器的问题是,当继承发挥作用时,一个类及其祖先都用这个装饰器装饰。

@autoValidate
class Parent 
  @t.IsNumber()
  readonly age: number;

  @t.IsString()
  readonly name: string;

  constructor(age: number, name: string) 
    this.age = age;
    this.name = name;
  


@autoValidate
class Child extends Parent 
  @t.IsBoolean()
  readonly happy: boolean;

  constructor(age: number, name: string, happy: boolean) 
    super(age, name);

    this.happy = happy;
  

Child被实例化时,当superChild的构造函数/装饰器中被调用时,验证器将在Parent的构造函数装饰器中抛出错误,因为happyundefined

如何更改我的装饰器,使其仅在thistarget 的实例而不是target 的后代时才运行其验证代码?

【问题讨论】:

if (this.constructor === target)? @PatrickRoberts 我认为你很接近,但 this.constructor 将是包装类,永远不会等于 target,因此装饰器需要保存包装类,以便可以比较 this.constructor给它。想写一个更正后的答案吗? 【参考方案1】:

基于 Patrick Roberts 想法的解决方案,额外保护防止定义子类和忘记 @autoValidate(否则将导致根本没有验证):

import * as t from 'class-validator';

interface Class 
  new(...args: any[]): ;


const lastClassWithValidation = Symbol();
export function autoValidate<T extends Class>(target: T) 
  return class extends target 
    static [lastClassWithValidation] = target;
    constructor(...args: any[]) 
      super(...args);

      if ((<any>this.constructor)[lastClassWithValidation] === target) 
        const errors = t.validateSync(this);

        if (errors.length > 0) 
          throw errors;
          
      
    
  ;

【讨论】:

在我看来,使用if (Object.getPrototypeOf(&lt;any&gt;this.constructor) === target) 不会那么麻烦,这样您就不会用实现细节污染类的静态属性。 @PatrickRoberts 这种方式不提供对没有@autoValidate 的子类的保护。如果不需要这种保护,当然可以。如果是这样,自然会使用符号。我不相信拥有一个以字符串命名的静态属性有什么大不了的,但是使用符号很容易,所以我更新了答案。 在将类序列化为 JSON 时使用符号很好地避免成员被拾取。

以上是关于如何让我的类装饰器只在继承链中的最外层类上运行?的主要内容,如果未能解决你的问题,请参考以下文章

同一架构中的不同继承类型

JQuery 在不同的类上运行

如何管理不间断运行的线程

TypeScript中的类』五分钟掌握ts的类,你也能月入1.5w+

如何在派生类上动态调用静态方法

如何在派生的python类上操作装饰器?