全部学科
Python全栈
python
NodeJS全栈
nodejs
小程序首页
📅 2026-05-21 10 分钟 ✍️ juanwangdev

TypeScript 装饰器详解

装饰器是实验性功能,用于声明式地添加横切逻辑,下面梳理各类装饰器用法。

注意:需在 tsconfig.json 中开启 "experimentalDecorators": true

类装饰器

类装饰器接收构造函数作为参数,可修改或替换它:

TypeScript
function sealed(constructor: Function) {
  Object.seal(constructor);
  Object.seal(constructor.prototype);
}

@sealed
class Greeter {
  greet() { console.log("Hello"); }
}

装饰器工厂(返回装饰器函数)

TypeScript
function logClass(prefix: string) {
  return function (constructor: Function) {
    console.log(`${prefix} ${constructor.name} created`);
  };
}

@logClass("Creating:")
class UserService {}
// 输出:Creating: UserService created

方法装饰器

方法装饰器接收三个参数:原型对象、方法名、属性描述符:

TypeScript
function log(
  target: any,
  propertyKey: string,
  descriptor: PropertyDescriptor
) {
  const original = descriptor.value;
  descriptor.value = function (...args: any[]) {
    console.log(`Calling ${propertyKey} with`, args);
    return original.apply(this, args);
  };
}

class Calculator {
  @log
  add(a: number, b: number): number {
    return a + b;
  }
}

const calc = new Calculator();
calc.add(1, 2);  // 输出:Calling add with [1, 2]

属性装饰器

属性装饰器接收两个参数:原型对象、属性名:

TypeScript
function Min(min: number) {
  return function (target: any, propertyKey: string) {
    let value: number;
    Object.defineProperty(target, propertyKey, {
      get() { return value; },
      set(newVal: number) {
        if (newVal < min) {
          throw new Error(`${propertyKey} must be >= ${min}`);
        }
        value = newVal;
      }
    });
  };
}

class Product {
  @Min(0)
  price!: number;
}

const p = new Product();
// p.price = -5;  // ❌ Error
p.price = 10;     // ✅

访问器装饰器

与方法装饰器用法相同,应用于 getter/setter:

TypeScript
function enumerable(value: boolean) {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor
  ) {
    descriptor.enumerable = value;
  };
}

class Person {
  constructor(private _name: string) {}

  @enumerable(false)
  get name() { return this._name; }
}

const p = new Person("Alice");
console.log(Object.keys(p));  // [](name 不可枚举)

参数装饰器

参数装饰器接收三个参数:原型对象、方法名、参数索引:

TypeScript
function Required(target: any, propertyKey: string, parameterIndex: number) {
  console.log(`${propertyKey} param ${parameterIndex} is required`);
}

class AuthService {
  login(@Required username: string, @Required password: string) {
    console.log(`Login: ${username}`);
  }
}
// 输出:login param 0 is required
//       login param 1 is required

参数装饰器与元数据结合

TypeScript
const REQUIRED_METADATA_KEY = "required_params";

function Required(target: any, propertyKey: string, parameterIndex: number) {
  const existing = Reflect.getMetadata(REQUIRED_METADATA_KEY, target, propertyKey) || [];
  existing.push(parameterIndex);
  Reflect.defineMetadata(REQUIRED_METADATA_KEY, existing, target, propertyKey);
}

装饰器执行顺序

TypeScript
function classDec(constructor: Function) { console.log("Class"); }
function methodDec(target: any, key: string, desc: PropertyDescriptor) { console.log("Method"); }
function propDec(target: any, key: string) { console.log("Property"); }
function paramDec(target: any, key: string, idx: number) { console.log("Param"); }

@classDec
class Foo {
  @propDec bar: string;

  @methodDec
  test(@paramDec x: number) {}
}
// 执行顺序:Property → Param → Method → Class

规则:从上到下,从外到内。类装饰器最后执行,参数装饰器最先。

要点总结

  • 类装饰器接收构造函数,可修改/替换/扩展它
  • 方法装饰器通过 descriptor.value 包装原方法实现横切逻辑
  • 属性装饰器通过 Object.defineProperty 重写 getter/setter
  • 访问器装饰器与方法装饰器相同,应用于 getter/setter
  • 参数装饰器记录参数索引,常与元数据反射结合使用
  • 装饰器执行顺序:属性 → 参数 → 方法 → 类(从外到内)

📝 发现内容有误?点击此处直接编辑

← 上一篇 TypeScript 装饰器工厂与元数据反射
下一篇 → TypeScript 常用内置工具类型
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库