TypeScript装饰器详解:像搭积木一样给代码加功能

作为一名前端开发者,你一定遇到过这样的场景:

想给类加一个日志功能,却要每个方法都写console.log()

想验证参数是否合法,却要在每个方法里重复判断。

TypeScript的装饰器就像给代码贴"魔法贴纸",能优雅地解决这些问题。本文将带你从零掌握装饰器的5种类型,附带真实案例,看完就能直接用。


一、类装饰器:给整个类加功能

1.1 参数说明

javascript 复制代码
function 类装饰器(target: Function): void | Function
  • target:当前类的构造函数(相当于类的身份证)

1.2 实战案例:记录类创建时间

javascript 复制代码
// 创建装饰器
function 日志类(target: Function) {
  target.prototype.创建时间 = new Date().toLocaleString();
}

// 使用装饰器
@日志类
class 用户 {
  获取创建时间() {
    return this.创建时间;
  }
}

const user = new 用户();
console.log(user.获取创建时间()); // 输出类似 "2025-10-28 15:30:00"

💡 通过修改类的原型,我们给所有实例免费加了创建时间属性。


二、方法装饰器:修改方法行为

2.1 参数说明

typescript 复制代码
function 方法装饰器(
  target: Object,          // 类的原型
  methodName: string,      // 方法名
  descriptor: PropertyDescriptor // 方法配置
): void | PropertyDescriptor

2.2 实战案例:给方法加权限校验

typescript 复制代码
function 需要登录(target: any, methodName: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;
  
  descriptor.value = function(...args) {
    if (!this.用户是否登录) {
      throw new Error("请先登录");
    }
    return originalMethod.apply(this, args);
  };
  
  return descriptor;
}

class 服务 {
  用户是否登录 = false;
  
  @需要登录
  获取敏感数据() {
    return "机密信息";
  }
}

const service = new 服务();
service.获取敏感数据(); // 报错:请先登录

🛡️ 通过包装原方法,我们实现了权限控制而无需修改业务逻辑。


三、属性装饰器:标记或初始化属性

3.1 参数说明

typescript 复制代码
function 属性装饰器(
  target: Object,          // 类的原型
  propertyName: string    // 属性名
): void

3.2 实战案例:标记必填字段

typescript 复制代码
function 必填(target: any, propertyName: string) {
  const descriptor = {
    get() {
      throw new Error(`${propertyName} 是必填字段`);
    },
    set(value: any) {
      if (value === undefined) {
        throw new Error(`${propertyName} 不能为空`);
      }
      target[propertyName] = value;
    }
  };
  Object.defineProperty(target, propertyName, descriptor);
}

class 表单 {
  @必填
  用户名!: string;
}

const form = new 表单();
form.用户名 = "张三"; // 正常
console.log(form.用户名); // 输出 "张三"

form.用户名 = undefined; // 报错:用户名 不能为空

✅ 通过重写getter/setter,我们实现了字段校验。


四、参数装饰器:记录参数信息

4.1 参数说明

typescript 复制代码
function 参数装饰器(
  target: Object,              // 类的原型
  methodName: string | undefined, // 方法名(构造函数为undefined)
  parameterIndex: number      // 参数位置(从0开始)
): void

4.2 实战案例:记录参数来源

typescript 复制代码
function 参数追踪(target: any, methodName: string, parameterIndex: number) {
  console.log(`方法 ${methodName} 的第 ${parameterIndex} 个参数被调用`);
}

class 计算器 {
  加法(@参数追踪 a: number, @参数追踪 b: number) {
    return a + b;
  }
}

const calc = new 计算器();
calc.加法(10, 20); // 控制台输出:
// 方法 加法 的第 0 个参数被调用
// 方法 加法 的第 1 个参数被调用

📊 这个功能在调试时特别有用,能快速定位参数传递路径。


五、访问器装饰器:控制属性访问

5.1 参数说明

typescript 复制代码
function 访问器装饰器(
  target: Object,          // 类的原型
  name: string,            // 属性名
  descriptor: PropertyDescriptor // 访问器配置
): void | PropertyDescriptor

5.2 实战案例:缓存get方法结果

ini 复制代码
function 缓存(target: any, name: string, descriptor: PropertyDescriptor) {
  const originalGetter = descriptor.get;
  
  const cacheKey = `$$${name}Cache`;
  
  descriptor.get = function() {
    if (this[cacheKey] === undefined) {
      this[cacheKey] = originalGetter.call(this);
    }
    return this[cacheKey];
  };
  
  return descriptor;
}

class 费波那契 {
  private n = 10;
  
  @缓存
  get 第n项() {
    // 模拟耗时计算
    let result = 0, a = 1, b = 1;
    for (let i = 3; i <= this.n; i++) {
      result = a + b;
      a = b;
      b = result;
    }
    return b;
  }
}

const fib = new 费波那契();
console.log(fib.第n项); // 第一次计算
console.log(fib.第n项); // 直接返回缓存结果

⚡ 通过缓存getter结果,避免重复计算。


六、使用装饰器的注意事项

  1. 启用配置 :在tsconfig.json中添加

    json 复制代码
    {
      "compilerOptions": {
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true
      }
    }
  2. 执行顺序 :多个装饰器按从下往上执行

    swift 复制代码
    @装饰器A
    @装饰器B
    class 示例 {} 
    // 实际执行顺序是:装饰器B → 装饰器A
  3. 参数装饰器限制 :不能用于getter/setter的参数


七、装饰器应用场景

场景 推荐装饰器类型
权限控制 方法装饰器
参数校验 参数装饰器
缓存结果 方法/访问器装饰器
字段验证 属性装饰器
日志记录 类/方法装饰器

八、总结

装饰器就像给代码贴"功能补丁",能让你:

  • 解耦业务逻辑:把日志、权限等公共逻辑抽离
  • 提升代码可维护性:修改装饰器就能影响所有相关代码
  • 实现AOP编程:在不修改原代码的情况下增强功能

建议从简单场景开始尝试,比如先用方法装饰器加日志,逐步探索更复杂的用法。掌握装饰器后,你会发现很多重复代码都可以优雅地消失了!

相关推荐
__zRainy__几秒前
解决pnpm v10+不自动构建
前端·pnpm·工程化
猫猫不是喵喵.38 分钟前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~2 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华2 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子5 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅5 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni5 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学6 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端
赵庆明老师7 小时前
Vben精讲:21-详解web-antd:tsconfig.json
前端·json·vim