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

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

相关推荐
OpenTiny社区3 小时前
如何使用 TinyEditor 快速部署一个协同编辑器?
前端·vue.js
ttyyttemo3 小时前
加载图片,不同数据源,Compose实现
前端
嗷呜~3 小时前
error 找不到模块“../views/Login.vue”或其相应的类型声明
typescript·vue3
Mike_jia3 小时前
Dumbterm:基于网页的终端革命!随时随地安全访问服务器的终极方案
前端
看今朝·3 小时前
【Dash框架】Dash回调函数中Output的属性详解
java·前端·dash
Data_Adventure3 小时前
文件Base64转换工具升级:从图片到多格式文件的全新体验
前端
D11_3 小时前
【React】验证码图片管理系统
前端
掘金安东尼4 小时前
Caddyfile:用最简单的方式配置最现代的 Web 服务器
运维·服务器·前端
菠萝+冰4 小时前
React-Window 虚拟化滚动
前端·react.js·前端框架