← 返回题目列表

TypeScript 装饰器是什么?常见使用场景有哪些?

高频 中等 第 19 / 32 题 更新于 2026/07/29
TypeScript装饰器元编程

简化版

装饰器是一种给类、方法、属性、参数附加元信息或包装行为的语法,常见于依赖注入、路由声明、ORM、日志和权限控制。TypeScript 装饰器依赖编译配置,不同装饰器提案阶段的语义也要区分。

详细版

常见写法:

@Controller('/users')
class UserController {
  @Get('/:id')
  findUser() {}
}

装饰器本质是函数。 它可以读取或修改被装饰目标的描述信息。 面试重点是:装饰器不是普通注释,它会参与编译和运行;使用前要确认 experimentalDecorators、元数据配置和框架语义。

完整版教学

一、装饰器解决声明式附加能力的问题

很多框架需要给类或方法附加额外信息。 例如这个类是控制器,这个方法对应 GET 路由,这个属性需要注入依赖。 如果不用装饰器,就要写大量配置对象或手工注册代码。

@Controller('/api')
class ApiController {}

装饰器让这些信息贴在目标代码旁边。 读者看到类声明,就能看到它在框架中的角色。 这是一种声明式元编程风格。

二、装饰器本质上是函数调用

装饰器语法看起来像注解,但它本质是函数。 类装饰器接收构造函数。 方法装饰器通常接收原型、方法名和属性描述符。

function Log(target: any, key: string, descriptor: PropertyDescriptor) {
  const original = descriptor.value;
  descriptor.value = function (...args: unknown[]) {
    console.log('call', key);
    return original.apply(this, args);
  };
}

class Service {
  @Log
  run() {}
}

上面例子里,方法调用前会多输出 1 条日志。 这说明装饰器可以包装行为,不只是记录元数据。

三、装饰器工厂可以接收参数

很多装饰器需要配置。 例如路由路径、权限名、缓存时间。 这时会写成装饰器工厂:外层函数接收配置,内层函数才是真正装饰器。

function Cache(ttl: number) {
  return function (_target: any, key: string, descriptor: PropertyDescriptor) {
    console.log(`cache ${key} for ${ttl}ms`);
  };
}

class Repo {
  @Cache(3000)
  list() {}
}

流程如下:

读取 @Cache(3000)
  -> 先调用 Cache 得到装饰器函数
  -> 再把目标信息传给装饰器函数

这就是为什么 @Get('/users') 能传路由路径。

四、常见使用场景来自框架

装饰器在业务代码里不一定每天手写,但在框架里很常见。 NestJS 用它声明控制器、模块和依赖注入。 Angular 用它声明组件和服务。 ORM 用它声明实体字段和关系。

场景装饰器作用
路由标记 URL 和 HTTP 方法
依赖注入标记可注入类和注入点
ORM标记表、列、关系
日志权限包装方法调用
校验给字段附加校验规则

这些场景共同点是:需要把元信息绑定到类结构上。

五、reflect-metadata 和元数据

很多旧版 TypeScript 装饰器生态会配合 reflect-metadata。 它可以在运行时读取设计类型等元信息。 但类型信息能保留到什么程度有限,泛型等复杂类型通常会被擦除。

// 常见配置
// "experimentalDecorators": true
// "emitDecoratorMetadata": true

记忆钩子:装饰器能把元信息挂到代码结构上,但 TypeScript 类型不会完整搬到运行时。

如果面试追问“装饰器能不能拿到泛型 T”,要说明泛型编译后会擦除,不能指望运行时完整读取。

六、标准装饰器和旧装饰器要区分

JavaScript 装饰器标准经历过变化。 TypeScript 早期支持的是 experimental decorators,生态里很多框架基于旧语义。 较新的标准装饰器语义与旧版存在差异。

旧 TS experimental decorators
  -> Angular / NestJS 等生态大量使用
新 ECMAScript decorators
  -> 标准化方向,语义不同

工程里不要只看语法能不能写。 要看 TypeScript 版本、tsconfig、Babel 配置、框架要求是否一致。 装饰器一旦配置错,可能编译能过但运行时元数据缺失。

七、常见误区与追问

  • 误区:装饰器只是注释。 装饰器是函数调用,可以记录元数据或修改行为。
  • 误区:装饰器能拿到完整 TypeScript 类型。 TS 类型大多会擦除,运行时只能拿到有限元数据。
  • 误区:所有装饰器语义都一样。 旧 TS experimental decorators 和标准装饰器语义有差异。
  • 追问:装饰器工厂是什么? 返回装饰器函数的函数,用来给装饰器传配置参数。
  • 追问:为什么 NestJS 大量使用装饰器? 它需要声明模块、控制器、路由、依赖注入等元信息。
  • 追问:使用装饰器要看哪些配置? 至少关注 experimentalDecoratorsemitDecoratorMetadata、TS/Babel 版本和框架要求。

八、加强记忆

装饰器按“函数、目标、元信息”来记:它是函数,作用在类或成员上,用来附加元信息或包装行为。面试中不要只说语法糖,要补配置、运行时元数据和新旧语义差异。