@decorator 装饰器
最后更新:2026-10-22
@decorator 装饰器
装饰器是加在类、方法、属性、参数前的特殊语法 @name:用一层函数包装给类附加行为(日志、校验、依赖注入),不用改动类本身的代码。TS 里装饰器仍是实验性特性,需要 tsconfig 开启 experimentalDecorators;NestJS、TypeORM 等框架大量使用,是读懂这类框架源码的必修课。
| 分组 | 工程与实战 |
|---|---|
| 类型 | 语法 |
| 严格模式 | 不需要 |
ℹ️ 说明 编辑器将 TypeScript 转译为 JavaScript 后运行(不做类型检查);类型检查结果见示例 Output。
📝 语法
// 类装饰器:接收构造函数
function sealed(Ctor: Function): void {
Object.seal(Ctor);
Object.seal(Ctor.prototype);
}
// 方法装饰器:接收 (原型, 方法名, 属性描述符)
function logged(target: object, key: string, desc: PropertyDescriptor): void {
const original = desc.value;
desc.value = function (...args: unknown[]) {
console.log("call", key, args);
return original.apply(this, args);
};
}
@sealed
class Calculator {
@logged
add(a: number, b: number) { return a + b; }
}
⚙️ 参数
| ClassDecorator | 参数是类构造函数;可修改类、返回新类替换原类,@sealed 这类"整体约束"常用它 |
|---|---|
| MethodDecorator | 在类定义时执行一次,通过改写 desc.value 包装原方法;返回新描述符可整体替换 |
| experimentalDecorators | tsconfig 开关,不加会报"装饰器是实验特性"错误;框架生态(NestJS 等)当前普遍基于此版本 |
返回值:类装饰器可返回一个新构造函数替换原类(不返回则沿用原类);方法装饰器可返回新的属性描述符,或返回 undefined 仅通过副作用修改。
▶ 示例
修改下面的代码,点击试一试查看转译运行结果:
输出:
-> add(2, 3)
add = 5
-> mul(4, 5)
mul = 20
(转译后运行输出;方法装饰器在类定义时就完成包装——调用时先打印 -> add(2, 3) 再返回结果)
💡 要点
ℹ️ 说明 执行时机是"类定义时"而非"方法调用时":装饰器函数在 class 声明被求值时就运行一次,把 desc.value 换成包装后的版本;之后每次调用走的都是包装版。
💡 提示 需要给装饰器传参数时用"装饰器工厂":@log("debug") 是一个返回装饰器函数的函数——外层收配置,内层收 (target, key, desc)。NestJS 的 @Injectable() 带括号就是工厂。
⚠️ 注意 实验版与标准版签名不兼容:TS 5 起可以不用 experimentalDecorators 而使用 ES 标准装饰器,方法装饰器第二参数从 (target, key, desc) 变成 context 对象。阅读老项目/框架代码前先确认用的是哪一版。
🏷️ 相关工程与实战
💡 相关案例
更多案例即将上线。
📚 相关教程
❓ 常见问题
Q 装饰器和普通的高阶函数有什么区别?
A 本质都是"函数包函数"。区别在声明位置:装饰器写在被装饰者上方,编译器负责把它接到定义处,类/方法本体保持干净;高阶函数则要在创建处显式包一层(const add = logged(Calculator.prototype.add))。装饰器更像"贴标签",适合横切关注点。
Q NestJS 里的 @Injectable() 是怎么工作的?
A @Injectable() 是装饰器工厂,返回的类装饰器给类挂上元数据(标记它可被依赖注入容器实例化)。容器在创建 Controller 时读取构造函数参数的类型/装饰器元数据,自动解析并注入依赖——装饰器本身不做注入,只是登记信息。
Q 新项目该用实验版还是标准版装饰器?
A 看框架:跟随 NestJS/TypeORM 等生态就开 experimentalDecorators(它们目前依赖旧语义);纯自有代码推荐标准版(TS 5+ 默认),语义已进 ES 标准、不再有"实验"包袱,但两版写法不能混用。