TypeScript装饰器间通信:实现支持忽略参数的缓存装饰器
实现带参数忽略功能的TypeScript缓存装饰器
核心思路
要让@CachedParamIgnore告知@Cached哪些参数需要忽略,核心是通过参数装饰器记录被标记参数的索引,存储到方法的元数据中,再由方法装饰器读取这些索引,生成缓存Key时跳过对应参数。
具体实现
1. 实现@CachedParamIgnore参数装饰器
这个装饰器负责标记需要忽略的参数,并把参数索引存入目标方法的元数据:
import 'reflect-metadata'; // 定义专属元数据键,用于存储忽略的参数索引 const IGNORED_PARAMS_KEY = Symbol('cached:ignored-params'); export function CachedParamIgnore() { return function (target: any, methodName: string | symbol, paramIndex: number) { // 获取已有的忽略索引数组,无则初始化空数组 const ignoredParams = Reflect.getMetadata(IGNORED_PARAMS_KEY, target, methodName) || []; ignoredParams.push(paramIndex); // 更新元数据 Reflect.defineMetadata(IGNORED_PARAMS_KEY, ignoredParams, target, methodName); }; }
2. 实现@Cached方法装饰器
在这个装饰器里读取元数据中的忽略索引,生成缓存Key时过滤掉对应参数:
export function Cached() { return function (target: any, methodName: string | symbol, descriptor: PropertyDescriptor) { const originalMethod = descriptor.value; // 缓存容器,键为处理后的参数组合,值为方法返回结果 const cache = new Map<string, any>(); descriptor.value = function (...args: any[]) { // 获取需要忽略的参数索引 const ignoredParams = Reflect.getMetadata(IGNORED_PARAMS_KEY, target, methodName) || []; // 过滤被标记的参数,生成缓存Key const filteredArgs = args.filter((_, index) => !ignoredParams.includes(index)); const cacheKey = JSON.stringify(filteredArgs); // 缓存命中直接返回,否则执行原方法并缓存结果 if (cache.has(cacheKey)) { return cache.get(cacheKey); } const result = originalMethod.apply(this, args); cache.set(cacheKey, result); return result; }; return descriptor; }; }
3. 使用示例
class BarFoo { @Cached() doComplexCalc(@CachedParamIgnore() a: number, b: number, c: string) { console.log('执行复杂计算'); return a + b + c.length; } } const instance = new BarFoo(); instance.doComplexCalc(1, 2, 'test'); // 打印"执行复杂计算",返回5 instance.doComplexCalc(100, 2, 'test'); // 直接返回5,不执行计算(a被忽略,b、c参数一致) instance.doComplexCalc(1, 3, 'test'); // 打印"执行复杂计算",返回6
注意事项
- 必须在
tsconfig.json中开启编译选项:
{ "compilerOptions": { "experimentalDecorators": true, "emitDecoratorMetadata": true } }
- 如果参数是复杂对象,
JSON.stringify可能无法生成唯一Key,可替换为自定义序列化逻辑(比如结合lodash.isEqual和哈希函数)。 - 上述实现中所有类实例共享同一缓存,若需每个实例独立缓存,可将
cache定义在实例对象上,而非装饰器闭包内。
内容的提问来源于stack exchange,提问作者Jeanluca Scaljeri
相关产品推荐
相关产品推荐

