如何使我的类装饰器仅在继承链中最外面的类上运行?

时间:2018-10-23 21:45:57

标签: javascript typescript inheritance decorator

我正在使用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时,在Parent的构造函数/装饰器中调用super时,验证器将在Child的构造函数的装饰器中引发错误,因为{{ 1}}是happy

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

1 个答案:

答案 0 :(得分:2)

基于帕特里克·罗伯茨(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;
        }  
      }
    }
  };
}