装饰器与元数据

装饰器:增强类的元编程能力

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的装饰器和元数据为面向对象编程提供了强大的元编程能力,使得我们可以:

  1. 通过装饰器优雅地修改类及其成员的行为
  2. 使用元数据在运行时获取和操作类型信息
  3. 实现依赖注入、AOP等高级编程模式
  4. 构建声明式的API和框架

这些特性使得TypeScript在构建大型复杂应用时更加灵活和可维护,是现代JavaScript开发中不可或缺的工具。