十二个例子让你学会TypeScript装饰器

44 阅读1分钟

WechatIMG588.png 理解 TypeScript 装饰器的关键是了解它们可以附加到类、方法、属性、参数等各种元素上,以修改它们的行为。以下是一些具体的 TypeScript 装饰器案例:

1. 类装饰器:添加元数据

类装饰器可以用来添加元数据或修改类的行为。例如,让我们创建一个用于标记类的装饰器,它会记录类的名称和一些元数据。

function classDecorator<T extends { new (...args: any[]): {} }>(constructor: T) {
  return class extends constructor {
    className = constructor.name;
  };
}

@classDecorator
class MyClass {
  constructor(public data: string) {}
}

const instance = new MyClass("Hello");
console.log(instance.className); // 输出 "MyClass"

2. 方法装饰器:修改方法行为

方法装饰器可以用来修改方法的行为。以下示例演示如何创建一个方法装饰器,用于限制方法的执行次数。

function limitExecutions(limit: number) {
  return function (
    target: any,
    key: string,
    descriptor: PropertyDescriptor
  ) {
    const originalMethod = descriptor.value;
    let executions = 0;

    descriptor.value = function (...args: any[]) {
      if (executions < limit) {
        executions++;
        return originalMethod.apply(this, args);
      } else {
        console.log(`Method ${key} reached execution limit.`);
      }
    };

    return descriptor;
  };
}

class Example {
  @limitExecutions(2)
  execute() {
    console.log("Method executed.");
  }
}

const instance = new Example();
instance.execute(); // 输出 "Method executed."
instance.execute(); // 输出 "Method executed."
instance.execute(); // 输出 "Method reached execution limit."

3. 属性装饰器:添加元数据

属性装饰器用于添加元数据或修改属性的行为。以下示例创建一个属性装饰器,用于添加属性的描述信息。

function propertyDescription(description: string) {
  return function (target: any, key: string) {
    Reflect.defineMetadata("description", description, target, key);
  };
}

class Example {
  @propertyDescription("This is a sample property.")
  data: string;
}

const description = Reflect.getMetadata("description", Example.prototype, "data");
console.log(description); // 输出 "This is a sample property."

4. 参数装饰器:验证参数

参数装饰器可以用来验证方法的参数。以下示例演示如何创建一个参数装饰器,用于检查参数是否为正数。

function positiveNumber(target: any, key: string, index: number) {
  const originalMethod = target[key];

  target[key] = function (...args: any[]) {
    if (typeof args[index] !== "number" || args[index] <= 0) {
      console.log(`Invalid argument at position ${index}: ${args[index]}`);
    } else {
      return originalMethod.apply(this, args);
    }
  };
}

class Example {
  add(@positiveNumber a: number, @positiveNumber b: number) {
    return a + b;
  }
}

const instance = new Example();
instance.add(5, 10); // 输出 15
instance.add(-2, 10); // 输出 "Invalid argument at position 0: -2"

5. 计时装饰器:记录方法执行时间

function timing(target: any, key: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
    const start = performance.now();
    const result = originalMethod.apply(this, args);
    const end = performance.now();
    console.log(`Method ${key} took ${end - start} milliseconds.`);
    return result;
  };

  return descriptor;
}

class Timer {
  @timing
  slowOperation() {
    for (let i = 0; i < 10000000; i++) {}
  }
}

const timer = new Timer();
timer.slowOperation(); // 输出方法执行时间

6. 验证装饰器:验证输入参数

function validate(target: any, key: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
    if (args.some(arg => typeof arg !== "number")) {
      console.error("Invalid argument type. Must be a number.");
      return;
    }
    return originalMethod.apply(this, args);
  };

  return descriptor;
}

class Calculator {
  @validate
  add(a: number, b: number) {
    return a + b;
  }
}

const calculator = new Calculator();
calculator.add(5, 10); // 输出 15
calculator.add(5, "10"); // 输出 "Invalid argument type. Must be a number."

7. 记录日志装饰器:记录方法调用日志

function log(target: any, key: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
    console.log(`Calling method ${key} with arguments: ${args}`);
    return originalMethod.apply(this, args);
  };

  return descriptor;
}

class Logger {
  @log
  logMessage(message: string) {
    console.log(message);
  }
}

const logger = new Logger();
logger.logMessage("Hello, World!"); // 输出方法调用日志

8. 身份验证装饰器:验证用户登录状态

function authenticate(target: any, key: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
    // 检查用户是否已登录
    if (isUserLoggedIn()) {
      return originalMethod.apply(this, args);
    } else {
      console.error("Authentication failed. Please log in.");
    }
  };

  return descriptor;
}

class AuthenticatedApp {
  @authenticate
  accessSecureData() {
    console.log("Accessing secure data.");
  }
}

const app = new AuthenticatedApp();
app.accessSecureData(); // 输出 "Accessing secure data.",因为用户已登录

// 模拟用户未登录
function isUserLoggedIn() {
  return false;
}
app.accessSecureData(); // 输出 "Authentication failed. Please log in."

9. 路由装饰器:定义路由信息

function route(path: string) {
  return function (target: any, key: string, descriptor: PropertyDescriptor) {
    // 将路由信息存储在元数据中
    Reflect.defineMetadata("route", path, target, key);
  };
}

class RouterExample {
  @route("/home")
  displayHome() {
    // 显示主页
  }

  @route("/about")
  displayAbout() {
    // 显示关于页面
  }
}

// 获取路由信息
const homeRoute = Reflect.getMetadata("route", RouterExample.prototype, "displayHome");
console.log(homeRoute); // 输出 "/home"

10. 缓存装饰器:缓存方法的结果

function cache(target: any, key: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;
  const cache = new Map();

  descriptor.value = function (...args: any[]) {
    const cacheKey = JSON.stringify(args);
    if (cache.has(cacheKey)) {
      return cache.get(cacheKey);
    } else {
      const result = originalMethod.apply(this, args);
      cache.set(cacheKey, result);
      return result;
    }
  };

  return descriptor;
}

class CachedCalculator {
  @cache
  add(a: number, b: number) {
    return a + b;
  }
}

const calculator = new CachedCalculator();
console.log(calculator.add(2, 3)); // 输出 5
console.log(calculator.add(2, 3)); // 输出 5(从缓存中获取)

11. 日志装饰器:记录方法执行日志

function logMethod(target: any, key: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
    console.log(`Calling method ${key} with arguments: ${JSON.stringify(args)}`);
    const result = originalMethod.apply(this, args);
    console.log(`Method ${key} returned: ${JSON.stringify(result)}`);
    return result;
  };

  return descriptor;
}

class LoggableClass {
  @logMethod
  performOperation(a: number, b: number) {
    return a + b;
  }
}

const loggable = new LoggableClass();
loggable.performOperation(3, 7); // 输出方法的执行日志

12. 跟踪装饰器:记录方法执行时间

function trackPerformance(target: any, key: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
    const start = performance.now();
    const result = originalMethod.apply(this, args);
    const end = performance.now();
    console.log(`Method ${key} took ${end - start} milliseconds to execute.`);
    return result;
  };

  return descriptor;
}

class PerformantClass {
  @trackPerformance
  timeConsumingOperation() {
    for (let i = 0; i < 100000000; i++) {}
  }
}

const performant = new PerformantClass();
performant.timeConsumingOperation(); // 输出方法的执行时间