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编程:在不修改原代码的情况下增强功能

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

相关推荐
To_OC4 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
vipbic4 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy4 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志5 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble6 小时前
c端系统,其实更像一个信息展示平台
前端
李姆斯7 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜7 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司8 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能
kaiking_g8 小时前
vue项目中,静态导入 vs 动态导入 详解
前端·javascript·vue.js
江华森8 小时前
Web 开发基础:HTTP 与 REST
前端·网络协议·http