typescript 39 lines · 8 steps

A TTL cache keyed by object identity

A generic cache uses a WeakMap so object keys are garbage-collected automatically while entries expire on a time-to-live.

Explained by highlit
1type CacheEntry<V> = {
2 value: V;
3 computedAt: number;
4};
5 
6export class ObjectCache<K extends object, V> {
7 private readonly store = new WeakMap<K, CacheEntry<V>>();
8 
9 constructor(private readonly ttlMs: number = Infinity) {}
10 
11 get(key: K): V | undefined {
12 const entry = this.store.get(key);
13 if (!entry) return undefined;
14 if (Date.now() - entry.computedAt > this.ttlMs) {
15 this.store.delete(key);
16 return undefined;
17 }
18 return entry.value;
19 }
20 
21 set(key: K, value: V): V {
22 this.store.set(key, { value, computedAt: Date.now() });
23 return value;
24 }
25 
26 has(key: K): boolean {
27 return this.get(key) !== undefined;
28 }
29 
30 getOrCompute(key: K, factory: (key: K) => V): V {
31 const cached = this.get(key);
32 if (cached !== undefined) return cached;
33 return this.set(key, factory(key));
34 }
35 
36 delete(key: K): boolean {
37 return this.store.delete(key);
38 }
39}
01 / 01
STEP 01

Walkthrough

Space play ←→ step click any line
Three takeaways
  1. 1A WeakMap lets object keys be reclaimed by GC, so the cache never leaks memory holding dead keys alive.
  2. 2Storing a timestamp with each entry turns a plain map into a TTL cache checked lazily on read.
  3. 3getOrCompute composes get and set into a single memoization primitive callers can build on.

Related explainers

typescript
import { registerLocaleData } from '@angular/common';
import localeFr from '@angular/common/locales/fr';
import localeFrExtra from '@angular/common/locales/extra/fr';
import localeDe from '@angular/common/locales/de';

Locale-aware bootstrapping in Angular

i18n localization dependency-injection
Intermediate 8 steps
typescript
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import * as Joi from 'joi';
 

Validating env config at boot in NestJS

configuration schema-validation environment-variables
Intermediate 8 steps
typescript
import { Inject, Injectable, Logger } from '@nestjs/common';
import { CACHE_MANAGER } from '@nestjs/cache-manager';
import { Cache } from 'cache-manager';
import { InjectRepository } from '@nestjs/typeorm';

A cache-aside country lookup in NestJS

cache-aside dependency-injection batch-lookup
Intermediate 8 steps
typescript
import { Injectable, effect, signal, computed } from '@angular/core';
 
interface Preferences {
  theme: 'light' | 'dark';

A signal-based preferences store in Angular

signals state-management persistence
Intermediate 7 steps
javascript
import { useState, useEffect, useCallback, useRef } from 'react';
 
const cache = new Map();
const inflight = new Map();

Building a stale-while-revalidate hook in React

caching request-deduplication custom-hooks
Advanced 10 steps
php
<?php
 
namespace App\Services;
 

Building a cached daily leaderboard in Laravel

caching aggregation eager-loading
Intermediate 9 steps

Share this explainer

Here's the card — post it anywhere.

A TTL cache keyed by object identity — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code