export class LRUCache { map = new Map() keys: T[] = [] constructor(readonly maxSize: number = Infinity) {} has(k: T) { return this.map.has(k) } get(k: T) { const v = this.map.get(k) if (v !== undefined) { this.keys.push(k as T) if (this.keys.length > this.maxSize * 2) { this.keys.splice(-this.maxSize) } } return v } set(k: T, v: U) { this.map.set(k, v) this.keys.push(k) if (this.map.size > this.maxSize) { this.map.delete(this.keys.shift() as T) } } } export function cached({ maxSize, getKey, getValue, }: { maxSize: number getKey: (args: Args) => T getValue: (args: Args) => V }) { const cache = new LRUCache(maxSize) const get = (...args: Args) => { const k = getKey(args) let v = cache.get(k) if (!v) { v = getValue(args) cache.set(k, v) } return v } get.cache = cache get.getKey = getKey get.getValue = getValue return get }