TypeScript 装饰器

装饰器(Decorator)是用 @表达式 书写的一种声明,可附加在类、方法、属性或参数上,在不修改原始类代码的前提下附加日志、校验、注入等横切逻辑。TypeScript 5.0 起支持标准装饰器,传统写法需先在 tsconfig.json 开启 experimentalDecorators: true

方法装饰器

方法装饰器接收三个参数:目标原型、方法名、属性描述符,可用来改写原方法:

function log(target: any, key: string, descriptor: PropertyDescriptor) {
  const original = descriptor.value;
  descriptor.value = function (...args: any[]) {
    console.log("调用 " + key + ",参数:" + JSON.stringify(args));
    return original.apply(this, args);
  };
}

class Calculator {
  @log
  add(a: number, b: number): number {
    return a + b;
  }
}
console.log(new Calculator().add(2, 3));
// 输出:调用 add,参数:[2,3]
// 输出:5

原方法被包了一层:先记录调用日志,再执行真正的加法。

类装饰器

类装饰器接收构造函数本身,可以观察、修改甚至替换类:

function sealed(constructor: Function) {
  Object.seal(constructor.prototype);
  console.log("密封类:" + constructor.name);
}

@sealed
class User {
  constructor(public name: string) {}
}
// 输出:密封类:User

常用于冻结类、注册到容器、注入静态属性等框架级操作。

属性与参数装饰器

属性装饰器接收(目标、属性名),参数装饰器接收(目标、方法名、参数序号):

function required(target: any, key: string) {
  console.log("必填属性:" + key);
}
function inject(target: any, key: string, index: number) {
  console.log("注入第 " + index + " 个参数");
}

class Mail {
  @required
  email: string = "";
  send(@inject receiver: string): void {}
}
// 输出:必填属性:email
// 输出:注入第 0 个参数

它们不能直接改动值,但可配合反射元数据实现校验框架、依赖注入容器。

多个装饰器的执行顺序

同一目标叠加多个装饰器时:表达式自上而下求值,装饰器函数自下而上执行(离成员最近的先执行):

function first(target: any, key: string) {
  console.log("first 执行");
}
function second(target: any, key: string) {
  console.log("second 执行");
}

class Demo {
  @first
  @second
  run(): void {}
}
// 输出:second 执行
// 输出:first 执行

搞清「自下而上执行」后,叠加多层装饰器时就不会对输出顺序感到意外。

小结:装饰器以声明式写法给类成员附加横切能力,日志、校验、路由、注入等框架特性都建立其上;牢记它是「类定义阶段执行的函数」即可驾驭大多数用法。

笔记加载中…