装饰器概述:双轨并行
本教程共 80 篇 · 第 67 篇 · 更新于 2026-08-10 · 约 10 分钟阅读
本节目标:搞清楚 TypeScript 装饰器为什么有”两套”——一套是历史的 legacy 模式,一套是 TC39 标准化的 Stage 3 版本。理解它们的来龙去脉、语法差异、生态现状,以及你在实际项目中该怎么选。
装饰器是干什么的
装饰器(Decorator)是一种元编程语法。简单说,它允许你在不修改类原始代码的情况下,给类或类的成员”附加”额外行为。
// 一个最简单的装饰器
function log(target: any) {
console.log("类被定义啦");
}
@log
class User {} // 控制台输出:类被定义啦
@log 放在 class User 上面,类定义时就会自动执行 log 函数。你不需要手动调用任何东西,装饰器替你做了”注入”这件事。
在 Angular、NestJS 这些框架里,你天天在写装饰器:
@Component({ ... }) // Angular 组件
@Injectable() // NestJS 可注入服务
@Entity() // TypeORM 实体
但你可能没意识到——这些框架用的装饰器,和 TypeScript 5.0 之后的标准装饰器,是两套完全不同的东西。
为什么有两套:一段 10 年的历史
事情要从 2014 年说起。
TypeScript 1.5 在 2015 年引入了装饰器支持。那时候 ECMAScript 标准委员会(TC39)也在讨论装饰器提案,TypeScript 就照着当时的提案草案实现了。使用方式很简单——在 tsconfig.json 里加上一行:
{
"compilerOptions": {
"experimentalDecorators": true
}
}
注意这个名字:experimental(实验性的)。TypeScript 团队非常清楚,TC39 的最终标准可能会变。
果不其然,TC39 的装饰器提案在接下来的 8 年里发生了翻天覆地的变化。2015 版草案、2016 版草案、2019 版草案……每一版都和前一个版本语法不兼容。TypeScript 一直保留着 2014 年的最初实现,也就是我们今天说的 legacy 装饰器。
2022 年 3 月,TC39 装饰器提案终于进入 Stage 3(候选阶段),语法基本稳定。TypeScript 5.0(2023 年 3 月发布)第一时间实现了这套标准语法——不需要任何编译选项,开箱即用。
Note所以今天 TypeScript 里有两套装饰器:
- Legacy 装饰器:需要
experimentalDecorators: true,语法基于 2014 年 TC39 草案。- 标准装饰器:TC39 Stage 3,TS 5.0 起默认可用,不需要额外配置。
语法差异速览
两套装饰器的函数签名完全不同。先看一个直观对比。
Legacy 装饰器(类装饰器):
// 接收一个参数:类的构造函数
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
标准装饰器(类装饰器):
// 接收两个参数:value(类本身)和 context(上下文对象)
function sealed(value: Function, context: ClassDecoratorContext) {
// 通过 context.kind 判断装饰器类型
if (context.kind === 'class') {
Object.seal(value);
Object.seal(value.prototype);
}
}
区别不止这些。往下看:
| 对比维度 | Legacy 装饰器 | 标准(Stage 3)装饰器 |
|---|---|---|
| 需要编译选项 | experimentalDecorators: true | 不需要(TS 5.0+) |
| 类装饰器参数 | (constructor) | (value, context) |
| 方法装饰器参数 | (target, propertyKey, descriptor) | (value, context) |
| 属性装饰器 | 有(拿不到值,功能受限) | 有(可返回初始化函数) |
| 参数装饰器 | 有 | 没有 |
accessor 关键字 | 不存在 | 有 auto-accessor |
| 多个装饰器顺序 | 由下往上执行 | 由下往上执行(一致) |
| 装饰器工厂 | 支持 | 支持 |
最关键的差异:两套装饰器不能混用。一个文件要么用 legacy,要么用标准。如果你在一个方法上写 @log,编译器必须根据文件级的配置来决定走哪套规则。
生态现状:大部分库还在用 legacy
截止 2026 年中,装饰器生态的格局很明确:
- NestJS:完全基于 legacy 装饰器(
@Controller、@Injectable、@Get等)。因为标准装饰器不支持参数装饰器,而 NestJS 的依赖注入严重依赖参数装饰器,短期内无法迁移。 - Angular:同样完全基于 legacy 装饰器(
@Component、@Input、@Output等),情况与 NestJS 类似。 - TypeORM:基于 legacy 装饰器(
@Entity、@Column等)。 - class-validator / class-transformer:基于 legacy 装饰器。
- 新项目 / 中小库:越来越多的新库开始支持或切换到标准装饰器。
简单总结:大型框架的迁移进度很慢。如果你是 Angular / NestJS 开发者,继续用 legacy 装饰器。如果是从零开始的小项目,可以考虑标准装饰器。
TypeScript 7.0 的兼容策略
TypeScript 7.0(2026 年 7 月发布)是 Go 原生移植的大版本(代号 Corsa)。虽然编译器从 JS 迁移到了 Go,但类型检查语义与 6.x 完全一致。
对装饰器而言,7.0 的策略是:
experimentalDecorators: true仍然有效:legacy 装饰器不会被移除。- 标准装饰器无需配置:和 5.0/6.x 一样,直接可用。
emitDecoratorMetadata: true仍然有效:继续支持reflect-metadata的元数据生成。
TipTypeScript 官方目前没有给出 legacy 装饰器的移除时间表。考虑到 Angular/NestJS 的生态体量,legacy 装饰器大概率会长期保留。你不需要急着迁移。
这两章怎么读
接下来五章的安排:
- 第 68-69 章:详解 legacy 装饰器。哪怕你以后转向标准装饰器,Angular/NestJS 的项目里天天都要跟它打交道。
- 第 70 章:详解 TC39 Stage 3 标准装饰器,包括
auto-accessor、上下文 API 等 legacy 没有的新特性。 - 第 71 章:讲迁移策略和选型建议,结合最新的生态动态帮你做决策。
装饰器是高级特性。如果你刚开始学 TypeScript,建议先掌握前面章节的基础类型和类,再回头啃这部分。