TypeScript 装饰器
装饰器(Decorator)是一种特殊类型的声明,它能够被附加到类声明、方法、访问器、属性或参数上。装饰器使用 @expression这种形式,expression求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。
通过装饰器,我们可以修改类的行为,甚至可以实现 AOP(面向切面编程)。TypeScript 支持 5 种装饰器:
类装饰器
类装饰器在类声明之前被声明。类装饰器应用于类构造函数,可以用来监视、修改或替换类定义。
function sealed(constructor: Function) {
  Object.seal(constructor);
  Object.seal(constructor.prototype);
}
@sealed
class BugReport {
  type = 'report';
  title: string;
  constructor(t: string) {
    this.title = t;
  }
}
上面代码中,@sealed就是一个装饰器。它修改了BugReport这个类的行为,为它加上了封印,阻止了添加新属性。
方法装饰器
方法装饰器声明在一个方法的声明之前。它会被应用到方法的属性描述符上,可以用来监视、修改或者替换方法定义。
function enumerable(value: boolean) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    descriptor.enumerable = value;
  };
}
class Greeter {
  greeting: string;
  constructor(message: string) {
    this.greeting = message;
  }
  @enumerable(false)
  greet() {
    return 'Hello, ' + this.greeting;
  }
}
上面代码中,@enumerable是一个装饰器,它修改了greet方法的描述对象,使得该方法不可枚举。
访问器装饰器
访问器装饰器声明在一个访问器的声明之前。访问器装饰器应用于访问器的属性描述符,可以用来监视、修改或替换一个访问器的定义。
function configurable(value: boolean) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    descriptor.configurable = value;
  };
}
class Point {
  private _x: number;
  private _y: number;
  constructor(x: number, y: number) {
    this._x = x;
    this._y = y;
  }
  @configurable(false)
  get x() {
    return this._x;
  }
  @configurable(false)
  get y() {
    return this._y;
  }
}
上面代码中,@configurable装饰器修改了x和y访问器的描述对象,使得它们不可配置。
属性装饰器
属性装饰器声明在一个属性声明之前。属性装饰器不能用在声明文件中,或者任何外部上下文里。
function format(formatString: string) {
  return function (target: any, propertyKey: string) {
    let value = this[propertyKey];
    function getter() {
      return formatString.replace('%s', value);
    }
    function setter(newVal: string) {
      value = newVal;
    }
    Object.defineProperty(target, propertyKey, {
      get: getter,
      set: setter,
    });
  };
}
class Greeter {
  @format('Hello, %s')
  greeting: string;
  constructor(message: string) {
    this.greeting = message;
  }
  greet() {
    return this.greeting;
  }
}
let g = new Greeter('world');
console.log(g.greet()); // "Hello, world"
上面代码中,@format装饰器修改了greeting属性的行为,使得它在获取和设置时都会进行格式化。
参数装饰器
参数装饰器声明在一个参数声明之前。参数装饰器应用于类构造函数或方法声明。
function required(target: Object, propertyKey: string | symbol, parameterIndex: number) {
  throw new Error(`Argument ${parameterIndex} of ${propertyKey.toString()} is required`);
}
class Greeter {
  greeting: string;
  constructor(message: string) {
    this.greeting = message;
  }
  greet(@required name: string) {
    return 'Hello ' + name + ', ' + this.greeting;
  }
}