# 介绍

随着 TypeScript 和 ES6 里引入了类,在一些场景下我们需要额外的特性来支持标注或修改类及其成员。 装饰器(Decorators)为我们在类的声明及成员上通过元编程语法添加标注提供了一种方式。 Javascript 里的装饰器目前处在建议征集的第二阶段,但在 TypeScript 里已做为一项实验性特性予以支持。

装饰器是一项实验性特性,在未来的版本中可能会发生改变。

有两种方式启用装饰器特性:

shell:

tsc --target ESNext --experimentalDecrators

tsconfig.json:

{
  "compilerOptions": {
    "target": "ESNext",
    "experimentalDecorators": true
  }
}

# 装饰器

装饰器是一种特殊类型的声明,它能够被附加到类声明,方法, 访问符,属性或参数上。 装饰器使用 @expression 这种形式, expression 求值后必须为一个函数,它会在运行时被调用,被装饰的声明信息做为参数传入。

例如,有一个 @foo 装饰器,我们会这样定义 foo 函数:

function fool(target: any) {
  // do somthing with 'target'
}

# 装饰器工厂

装饰器工厂就是一个函数,它返回一个简单的表达式,供装饰器在运行时调用。

function add(value: number) {
    return function(target: any) {
        // do somthing with target
    }
}

# 装饰器求值

类中不同声明上的装饰器将按以下规定的顺序应用:

  1. 参数装饰器,然后依次是方法装饰器,访问符装饰器,或属性装饰器应用到每个实例成员。
  2. 参数装饰器,然后依次是方法装饰器,访问符装饰器,或属性装饰器应用到每个静态成员。
  3. 参数装饰器应用到构造函数。
  4. 类装饰器应用到类。<br>

# 类装饰器

类装饰器在类声明之前被声明(紧靠着类声明)。 类装饰器应用于类构造函数,可以用来监视,修改或替换类定义。
如果类装饰器返回一个值,它会使用提供的构造函数来替换类的声明。

interface Greeting {
    hello: string
}
function setHello(target: any) {
  target.prototype.hello = "hello"  // 为类设置属性
}
@sayHello
class Greeting {
    constructor() ()
}
const greeting: Greeting = new Greeting();
console.log(greeting.hello);    // "hello"

# 方法装饰器

方法装饰器声明在一个方法的声明之前(紧靠着方法声明)。 它会被应用到方法的 属性描述符上,可以用来监视,修改或者替换方法定义。

方法装饰器需传入 3 个参数:

  • target: any 对于静态成员来说是类的构造函数,对于实例成员是类的原型对象
  • propertyKey: string | symbol 方法的名称
  • descriptor: PropertyDescriptor 方法的描述 修饰方法
function onRegister(): MethodDecorator {
  return function (
    target: any,
    propertyKey: string | symbol,
    descriptor: PropertyDescriptor,
  ) {
    console.log(target); // Person: {}
    console.log(propertyKey); // "foo"
    console.log(descriptor); // { "writable": true, "enumerable": false, "configurable": true }
    console.log(descriptor.value); // foo() { console.log('foo'); }
  };
}
class Person {
  name: string = "";
  @onRegister()
  foo() {
    console.log("foo");
  }
}
const p = new Person();

未完待续……

# 参考文献

[1] Typescript 中文网 | 装饰器