A sliding TTL cache expires each entry a fixed time after its last access, not after it was created — so a key that keeps getting read stays alive, while a key nobody touches for ttl milliseconds expires on its own. It is the caching rule behind idle session timeouts: stay signed in while you are active, time out once you walk away.
Implement slidingTtlCache(ttl), which returns an object with get, set, and has methods plus a size count. Every set and every live get restarts that entry's countdown; an entry that goes ttl milliseconds without any access expires.
function slidingTtlCache(ttl: number): {
get(key: unknown): unknown; // live -> slide deadline + return value; else undefined
set(key: unknown, value: unknown): void; // store + (re)start the countdown
has(key: unknown): boolean; // live? read-only peek, never slides
size: number; // count of live (non-expired) entries
};
const cache = slidingTtlCache(1000);
cache.set('token', 'abc');
// 900ms later — still inside the window:
cache.get('token'); // 'abc' (deadline slides to ~1900ms from set)
// another 900ms later (1800ms from set) — still alive, thanks to that read:
cache.get('token'); // 'abc'
const cache = slidingTtlCache(1000);
cache.set('draft', { title: 'Hi' });
cache.has('draft'); // true — a peek does not extend the deadline
// 1000ms of no access later:
cache.get('draft'); // undefined — expired and evicted
cache.size; // 0
get resets the entry's expiry to ttl ms from now, and set restarts it whenever it writes a key.has is a read-only peek — it reports whether a live entry exists but must not slide the deadline.expiresAt timestamp and treat an entry as gone once Date.now() reaches it, or use a setTimeout you restart on access; either is fine as long as access slides it.0, '', null, and false must store and read back, so decide liveness from the deadline, not the value's truthiness.Map is enough here.