Method Caching TTL Decorator
Cache expensive method results with a TTL decorator keyed by argument identity, tracking hit rates, and supporting explicit busting.
Code
interface CacheEntry<T> {
value: T;
expiresAt: number;
}
function Cached<T>(ttlMs: number) {
return function (
target: object,
propertyKey: string,
descriptor: TypedPropertyDescriptor<(...args: never[]) => Promise<T>>,
) {
const original = descriptor.value!;
const store = new Map<string, CacheEntry<T>>();
descriptor.value = async function (this: unknown, ...args: never[]) {
const key = propertyKey + ':' + JSON.stringify(args);
const hit = store.get(key);
const now = Date.now();
if (hit && hit.expiresAt > now) {
(this as { __cacheHits?: number }).__cacheHits ??= 0;
return hit.value;
}
const value = await original.apply(this, args);
store.set(key, { value, expiresAt: now + ttlMs });
return value;
};
return descriptor;
};
}
class ExchangeRates {
private hits = 0;
@Cached(30_000)
async ratesFor(base: string): Promise<Record<string, number>> {
const res = await fetch('https://api.example.com/rates?base=' + base);
return res.json();
}
@Cached(300_000)
async currencies(): Promise<string[]> {
return ['USD', 'EUR', 'JPY'];
}
}
const rates = new ExchangeRates();
const first = await rates.ratesFor('USD');
const second = await rates.ratesFor('USD');
console.log('cached identity:', first === second);Report a problem with this exampleOpens a pre-filled issue on GitHub.