当前位置: 首页 > news >正文

TypeScript 装饰器高级用法详解

TypeScript 中的装饰器提供了强大的元编程能力,可以用于实现各种高级模式。下面我将深入介绍装饰器的高级用法。

1. 装饰器组合与执行顺序

1.1. 多装饰器组合

function first() {console.log("first(): factory evaluated");return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {console.log("first(): called");};
}function second() {console.log("second(): factory evaluated");return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {console.log("second(): called");};
}class ExampleClass {@first()@second()method() {}
}// 输出顺序:
// first(): factory evaluated
// second(): factory evaluated
// second(): called
// first(): called

1.2. 执行顺序规则

1. 装饰器工厂从上到下执行;

2. 装饰器函数从下到上执行;

3. 不同类型的装饰器按特定顺序执行;

2. 元数据反射高级用法

结合 reflect-metadata 可以实现更强大的功能:

import "reflect-metadata";const requiredMetadataKey = Symbol("required");function required(target: Object, propertyKey: string | symbol, parameterIndex: number
) {const existingRequiredParameters: number[]= Reflect.getOwnMetadata(requiredMetadataKey, target, propertyKey) || [];existingRequiredParameters.push(parameterIndex);Reflect.defineMetadata(requiredMetadataKey, existingRequiredParameters, target, propertyKey);
}function validate(target: any, propertyName: string, descriptor: TypedPropertyDescriptor<Function>
) {const method = descriptor.value!;descriptor.value = function () {const requiredParameters: number[] = Reflect.getOwnMetadata(requiredMetadataKey, target, propertyName) || [];requiredParameters.forEach(parameterIndex => {if (parameterIndex >= arguments.length || arguments[parameterIndex] === undefined) {throw new Error(`Missing required argument at position ${parameterIndex}`);}});return method.apply(this, arguments);};
}class Greeter {@validategreet(@required name: string, title?: string) {return `Hello ${title || ''}${name}`;}
}const g = new Greeter();
g.greet("Alice"); // OK
g.greet(); // Error: Missing required argument at position 0

3. 方法拦截与AOP实现

3.1. 方法拦截器

function intercept(before: Function, after?: Function) {return function (target: any,propertyKey: string,descriptor: PropertyDescriptor) {const originalMethod = descriptor.value;descriptor.value = function (...args: any[]) {before.apply(this, args);const result = originalMethod.apply(this, args);if (after) after.apply(this, [result, ...args]);return result;};return descriptor;};
}class Calculator {@intercept((x: number, y: number) => console.log(`About to add ${x} and ${y}`),(result: number) => console.log(`Result is ${result}`))add(x: number, y: number): number {return x + y;}
}const calc = new Calculator();
calc.add(2, 3);
// About to add 2 and 3
// Result is 5

3.2. 异步方法拦截

function asyncIntercept() {return function (target: any,propertyKey: string,descriptor: PropertyDescriptor) {const originalMethod = descriptor.value;descriptor.value = async function (...args: any[]) {console.log(`Starting ${propertyKey} with args:`, args);try {const result = await originalMethod.apply(this, args);console.log(`Completed ${propertyKey} with result:`, result);return result;} catch (error) {console.error(`Error in ${propertyKey}:`, error);throw error;}};return descriptor;};
}class ApiService {@asyncIntercept()async fetchData(url: string) {const response = await fetch(url);return response.json();}
}

4. 依赖注入系统实现

4.1. 简易DI容器

import "reflect-metadata";const INJECTABLE_METADATA_KEY = Symbol("INJECTABLE_KEY");
const INJECT_METADATA_KEY = Symbol("INJECT_KEY");interface Type<T> {new(...args: any[]): T;
}class Container {private providers = new Map<symbol, any>();addProvider<T>(provider: T, identifier?: symbol) {const id = identifier || Symbol();this.providers.set(id, provider);return id;}inject(token: symbol) {return (target: any, key: string, index?: number) => {const metadata = Reflect.getMetadata(INJECT_METADATA_KEY, target) || {};metadata[key] = token;Reflect.defineMetadata(INJECT_METADATA_KEY, metadata, target);};}resolve<T>(target: Type<T>): T {const tokens = Reflect.getMetadata(INJECT_METADATA_KEY, target.prototype) || {};const injections = Object.keys(tokens).map(key => ({key,provider: this.providers.get(tokens[key])}));return new target(...injections.map(i => i.provider));}
}function Injectable() {return (target: any) => {Reflect.defineMetadata(INJECTABLE_METADATA_KEY, true, target);return target;};
}// 使用示例
const container = new Container();@Injectable()
class Logger {log(message: string) {console.log(`LOG: ${message}`);}
}const loggerToken = container.addProvider(new Logger());@Injectable()
class App {constructor(@container.inject(loggerToken) private logger: Logger) {}run() {this.logger.log("Application started");}
}const app = container.resolve(App);
app.run(); // 输出: LOG: Application started

5. 自定义装饰器工厂

5.1. 带参数的属性装饰器

function format(formatString: string) {return function (target: any, propertyKey: string) {let value = target[propertyKey];const getter = () => {return `${formatString} ${value}`;};const setter = (newVal: string) => {value = newVal;};Object.defineProperty(target, propertyKey, {get: getter,set: setter,enumerable: true,configurable: true,});};
}class Greeter {@format("Hello")greeting: string;constructor(message: string) {this.greeting = message;}
}const greeter = new Greeter("World");
console.log(greeter.greeting); // 输出: Hello World

5.2. 条件方法装饰器

function when(condition: boolean, decorator: MethodDecorator) {return function (target: any,propertyKey: string,descriptor: PropertyDescriptor) {if (condition) {return decorator(target, propertyKey, descriptor);}return descriptor;};
}class FeatureToggle {@when(process.env.NODE_ENV === 'development', log)experimentalMethod() {console.log("Running experimental feature");}
}

6. 装饰器与泛型结合

function logReturnType<T>() {return function (target: Object,propertyKey: string,descriptor: TypedPropertyDescriptor<(...args: any[]) => T>) {const originalMethod = descriptor.value!;descriptor.value = function (...args: any[]) {const result = originalMethod.apply(this, args);console.log(`Method ${propertyKey} returns type: ${typeof result}`);return result;};return descriptor;};
}class GenericExample {@logReturnType<number>()add(a: number, b: number): number {return a + b;}@logReturnType<string>()concat(a: string, b: string): string {return a + b;}
}const example = new GenericExample();
example.add(1, 2); // 输出: Method add returns type: number
example.concat("a", "b"); // 输出: Method concat returns type: string

7. 装饰器组合模式

function composeDecorators(...decorators: MethodDecorator[]) {return function (target: any,propertyKey: string,descriptor: PropertyDescriptor) {return decorators.reduce((desc, decorator) => {return decorator(target, propertyKey, desc) || desc;}, descriptor);};
}function logCall() {return function (target: any,propertyKey: string,descriptor: PropertyDescriptor) {const original = descriptor.value;descriptor.value = function (...args: any[]) {console.log(`Calling ${propertyKey} with`, args);return original.apply(this, args);};return descriptor;};
}function measureTime() {return function (target: any,propertyKey: string,descriptor: PropertyDescriptor) {const original = descriptor.value;descriptor.value = function (...args: any[]) {const start = performance.now();const result = original.apply(this, args);const end = performance.now();console.log(`Execution time: ${end - start}ms`);return result;};return descriptor;};
}class PerformanceExample {@composeDecorators(logCall(),measureTime())heavyCalculation(n: number) {let result = 0;for (let i = 0; i < n; i++) {result += Math.sqrt(i);}return result;}
}const perf = new PerformanceExample();
perf.heavyCalculation(1000000);
// 输出:
// Calling heavyCalculation with [1000000]
// Execution time: 12.345ms

8. 类装饰器扩展

8.1. 混入(Mixin)模式

type Constructor<T = {}> = new (...args: any[]) => T;function Timestamped<TBase extends Constructor>(Base: TBase) {return class extends Base {timestamp = Date.now();};
}function Loggable<TBase extends Constructor>(Base: TBase) {return class extends Base {log() {console.log(`Current timestamp: ${this.timestamp}`);}};
}class User {name: string;constructor(name: string) {this.name = name;}
}const TimestampedUser = Timestamped(User);
const LoggableTimestampedUser = Loggable(Timestamped(User));const user1 = new TimestampedUser("Alice");
console.log(user1.timestamp); // 输出当前时间戳const user2 = new LoggableTimestampedUser("Bob");
user2.log(); // 输出当前时间戳

9. 装饰器与React组件

// 高阶组件装饰器
function withLoadingIndicator(Component: React.ComponentType) {return function WithLoadingIndicator(props: any) {const [loading, setLoading] = React.useState(true);React.useEffect(() => {const timer = setTimeout(() => setLoading(false), 1000);return () => clearTimeout(timer);}, []);if (loading) {return <div>Loading...</div>;}return <Component {...props} />;};
}// 使用装饰器语法
@withLoadingIndicator
class MyComponent extends React.Component {render() {return <div>My Component Content</div>;}
}// 或者使用函数组件
const MyFunctionalComponent = withLoadingIndicator(() => (<div>My Functional Component</div>
));

10. 装饰器与性能优化

10.1. 记忆化(Memoization)装饰器

function memoize() {return function (target: any,propertyKey: string,descriptor: PropertyDescriptor) {const originalMethod = descriptor.value;const cache = new Map<string, any>();descriptor.value = function (...args: any[]) {const key = JSON.stringify(args);if (cache.has(key)) {console.log(`Cache hit for ${propertyKey}(${key})`);return cache.get(key);}const result = originalMethod.apply(this, args);cache.set(key, result);console.log(`Cache miss for ${propertyKey}(${key})`);return result;};return descriptor;};
}class ExpensiveOperations {@memoize()fibonacci(n: number): number {if (n <= 1) return n;return this.fibonacci(n - 1) + this.fibonacci(n - 2);}
}const ops = new ExpensiveOperations();
console.log(ops.fibonacci(10)); // 计算并缓存结果
console.log(ops.fibonacci(10)); // 从缓存读取

相关文章:

  • P10225 [COCI 2023/2024 #3] Milano C.le|普及
  • 深入浅出之STL源码分析6_模版编译问题
  • Kubernetes .yaml 文件配置
  • Kubernetes 集群部署应用
  • C PRIMER PLUS——第9节:动态内存分配、存储类别、链接和内存管理
  • 17前端项目----支付弹框
  • Three.js + React 实战系列 - 页脚区域 Footer 组件 ✨
  • vector--OJ1
  • Windows系统更新一键禁用:WindowsUpdateBlocker轻量级工具推荐
  • Typescript 源码核心流程
  • LeetCode 热题 100 101. 对称二叉树
  • 79.评论日记
  • UOJ 164【清华集训2015】V Solution
  • 元数据分类
  • 并发笔记-给数据上锁(二)
  • 怎样选择成长股 读书笔记(一)
  • Linux epoll 详解:概念、使用、数据结构、流程及应用
  • 力扣-二叉树-101 对称二叉树
  • 常见的 DCGM 设备级别指标及其含义
  • 一个网球新手的学习心得
  • 中美瑞士会谈后中国会否取消矿产出口许可要求?外交部回应
  • 这些网红果蔬正在收割你的钱包,营养师:吃了个寂寞
  • 人民时评:莫让“假俗乱”讲解侵蚀“文博热”
  • 综艺还有怎样的新可能?挖掘小众文化领域
  • 警方通报男子地铁上拍视频致乘客恐慌受伤:列车运行一度延误,已行拘
  • 巴基斯坦称对印精准打击造成设施损坏和人员伤亡