Loading lessons...
Project: Type-Safe Generic Cache & Event Emitter
Project 2: Type-Safe Generic Cache & Event Emitter
Build a generic memory cache with Time-To-Live (TTL) expiration and a fully typed Event Emitter library.
Typed Event Emitter Implementation
export type EventMap = Record<string, any>;
export class TypedEventEmitter<Events extends EventMap> {
private listeners: { [K in keyof Events]?: Array<(data: Events[K]) => void> } = {};
on<K extends keyof Events>(event: K, listener: (data: Events[K]) => void): void {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event]!.push(listener);
}
emit<K extends keyof Events>(event: K, data: Events[K]): void {
const handlers = this.listeners[event];
if (handlers) {
handlers.forEach((fn) => fn(data));
}
}
}
Generic Cache Implementation
interface CacheEntry<V> {
value: V;
expiresAt: number;
}
export class MemoryCache<K extends string | number, V> {
private store = new Map<K, CacheEntry<V>>();
set(key: K, value: V, ttlMs: number): void {
const expiresAt = Date.now() + ttlMs;
this.store.set(key, { value, expiresAt });
}
get(key: K): V | undefined {
const entry = this.store.get(key);
if (!entry) return undefined;
if (Date.now() > entry.expiresAt) {
this.store.delete(key);
return undefined;
}
return entry.value;
}
}
Usage Example
interface MyEvents {
userLoggedIn: { userId: string; timestamp: number };
cacheCleared: void;
}
const emitter = new TypedEventEmitter<MyEvents>();
emitter.on("userLoggedIn", (data) => {
console.log(`User ${data.userId} logged in at ${data.timestamp}`);
});
TL;DR
- Demonstrates mapped types for strongly typed event maps.
- Combines generic key/value constraints with runtime expiration logic.