装饰器:增强类的元编程能力
TypeScript中的装饰器(Decorator)是一种特殊类型的声明,它可以被附加到类声明、方法、属性或参数上,用于修改类的行为或添加元数据。装饰器使用@expression
形式,其中expression必须是一个函数,它会在运行时被调用。
类装饰器
类装饰器应用于类构造函数,可以用来观察、修改或替换类定义。例如:
typescript
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
方法装饰器
方法装饰器可以用于修改或替换方法定义:
typescript
function enumerable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.enumerable = value;
};
}
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
@enumerable(false)
greet() {
return "Hello, " + this.greeting;
}
}
元数据:为类添加额外信息
元数据(Reflect Metadata)是装饰器的常见应用场景之一,它允许我们在运行时获取和设置类型信息。要使用元数据功能,需要安装reflect-metadata
库:
bash
npm install reflect-metadata
基本元数据操作
typescript
import "reflect-metadata";
// 定义元数据
class Point {
@Reflect.metadata("design:type", Number)
x: number;
@Reflect.metadata("design:type", Number)
y: number;
}
// 获取元数据
const typeX = Reflect.getMetadata("design:type", Point.prototype, "x");
console.log(typeX); // [Function: Number]
自定义元数据
typescript
function logType(target: any, key: string) {
const t = Reflect.getMetadata("design:type", target, key);
console.log(`${key} type: ${t.name}`);
}
class Demo {
@logType
public attr1: string;
}
装饰器与元数据的实际应用
依赖注入
装饰器和元数据可以优雅地实现依赖注入:
typescript
import "reflect-metadata";
const Injectable = (): ClassDecorator => target => {};
class OtherService {
doSomething() {
console.log("Doing something...");
}
}
@Injectable()
class MyService {
constructor(private otherService: OtherService) {}
doWork() {
this.otherService.doSomething();
}
}
const factory = (target: any): any => {
const providers = Reflect.getMetadata("design:paramtypes", target) || [];
const args = providers.map((provider: any) => new provider());
return new target(...args);
};
const myService = factory(MyService);
myService.doWork();
路由装饰器
在Web框架中,装饰器可以简化路由定义:
typescript
const METHOD_METADATA = "method";
const PATH_METADATA = "path";
const Controller = (path: string): ClassDecorator => {
return target => {
Reflect.defineMetadata(PATH_METADATA, path, target);
};
};
const createMappingDecorator = (method: string) => (path: string): MethodDecorator => {
return (target, key, descriptor) => {
Reflect.defineMetadata(PATH_METADATA, path, descriptor.value!);
Reflect.defineMetadata(METHOD_METADATA, method, descriptor.value!);
};
};
const Get = createMappingDecorator("GET");
const Post = createMappingDecorator("POST");
@Controller("/test")
class SomeController {
@Get("/a")
someGetMethod() {
return "hello world";
}
@Post("/b")
somePostMethod() {}
}
总结
TypeScript的装饰器和元数据为面向对象编程提供了强大的元编程能力,使得我们可以:
- 通过装饰器优雅地修改类及其成员的行为
- 使用元数据在运行时获取和操作类型信息
- 实现依赖注入、AOP等高级编程模式
- 构建声明式的API和框架
这些特性使得TypeScript在构建大型复杂应用时更加灵活和可维护,是现代JavaScript开发中不可或缺的工具。