slogan3

Help me in het implementeren van een type-veilige caching-mechanisme in TypeScript voor {{cache_type}}.

description

Door het implementeren van een type-veilige caching-mechanisme verbeter je de prestaties van je toepassing terwijl je runtime-fouten gerelateerd aan type-mismatches minimaliseert. Deze aanpak bevordert betere onderhoudbaarheid en schaalbaarheid van de code.

prompt

try_prompt

Help me begrijpen hoe ik een type-veilig caching-mechanisme kan ontwerpen en implementeren in TypeScript voor {{cache_type}}. Geef gedetailleerde stappen, ... more
generate_helper
disclaimerOnPageApi image_legal_disclaimer...

ai_answers

provider: openai
model: gpt-4.1-nano
Natuurlijk! Hieronder vind je een uitgebreide gids om een type-veilig caching-mechanisme in TypeScript te ontwerpen en te implementeren, gericht op in-memory caching. Ik behandel de stappen, geef codevoorbeelden en deel beste praktijken. --- ### Stap 1: Bepaal het doel en ontwerp van de cache - **Type veiligheid**: De cache moet gegevens opslaan met strikt gedefinieerde types. - **In-memory**: Alles gebeurt in het geheugen, dus geen externe opslag. - **Efficiëntie**: Snelle lookup en eenvoudige beheermechanismen. - **Uitbreidbaarheid**: Mogelijkheid tot uitbreiding (bijv. verval, limieten). --- ### Stap 2: Definieer de generieke cache-klasse Gebruik generieke types zodat de cache voor verschillende data-types kan worden gebruikt. ```typescript class Cache<TKey, TValue> { private store: Map<TKey, TValue>; constructor() { this.store = new Map<TKey, TValue>(); } // Voeg een item toe set(key: TKey, value: TValue): void { this.store.set(key, value); } // Haal een item op get(key: TKey): TValue | undefined { return this.store.get(key); } // Verwijder een item delete(key: TKey): boolean { return this.store.delete(key); } // Controleer of een key bestaat has(key: TKey): boolean { return this.store.has(key); } // Leeg de cache clear(): void { this.store.clear(); } } ``` ### Stap 3: Gebruik type aliases of interfaces voor data Om de types te specificeren, kun je interfaces of type aliases maken. ```typescript interface User { id: number; naam: string; email: string; } type UserID = number; ``` ### Stap 4: Voorbeeld van het gebruik van de cache ```typescript const userCache = new Cache<UserID, User>(); // Toevoegen userCache.set(1, { id: 1, naam: "Jan", email: "jan@example.com" }); // Opvragen const user = userCache.get(1); if (user) { console.log(`Gebruiker: ${user.naam}`); } ``` --- ### Stap 5: Uitbreidingen voor meer functionaliteit #### 1. Vervaldatum (TTL - Time To Live) Voeg vervaltijd toe door timestamps op te slaan: ```typescript interface CacheEntry<T> { value: T; expiry?: number; // timestamp in ms } class TTLCache<TKey, TValue> { private store: Map<TKey, CacheEntry<TValue>> = new Map(); set(key: TKey, value: TValue, ttl?: number): void { const expiry = ttl ? Date.now() + ttl : undefined; this.store.set(key, { value, expiry }); } get(key: TKey): TValue | undefined { const entry = this.store.get(key); if (!entry) return undefined; if (entry.expiry && Date.now() > entry.expiry) { this.store.delete(key); return undefined; } return entry.value; } delete(key: TKey): boolean { return this.store.delete(key); } clear(): void { this.store.clear(); } } ``` --- ### Stap 6: Beste praktijken en tips - **Gebruik generieke classes**: Zo behoud je typeveiligheid en herbruikbaarheid. - **Implementeer vervalmechanismen**: Voor het voorkomen van verouderde data. - **Test uitgebreid**: Schrijf unittests om de cache-gedragingen te verifiëren. - **Overweeg limieten**: Bijvoorbeeld maximum aantal items om geheugen te beheren. - **Gebruik readonly types waar mogelijk**: Om mutatie te voorkomen. - **Documenteer je code**: Voor duidelijkheid en onderhoudbaarheid. --- ### Samenvatting Door gebruik te maken van generieke classes en strikte typing in TypeScript, bouw je een cache die niet alleen efficiënt en in-memory is, maar ook volledig type-veilig. Voeg eventueel verval- en limietmechanismen toe voor meer controle. --- Mocht je specifieke features willen toevoegen of verder willen uitbreiden, vraag gerust!
provider: deepseek
model: deepseek-chat
Zeker! Ik zal je helpen een type-veilig in-memory caching-mechanisme in TypeScript te ontwerpen. Hier is een complete implementatie met gedetailleerde uitleg. ## Stap 1: Basis Cache Interface en Type Definitie ```typescript // Cache interface met generieke types voor maximale typeveiligheid interface Cache<K, V> { set(key: K, value: V, ttl?: number): void; get(key: K): V | undefined; has(key: K): boolean; delete(key: K): boolean; clear(): void; size(): number; keys(): K[]; values(): V[]; } ``` ## Stap 2: Cache Item Type met TTL Ondersteuning ```typescript // Intern cache item met TTL (Time To Live) ondersteuning interface CacheItem<V> { value: V; expiresAt: number | null; // Timestamp in milliseconds } // Type guard om te controleren of een item nog geldig is function isValidCacheItem<V>(item: CacheItem<V> | undefined): item is CacheItem<V> { if (!item) return false; if (item.expiresAt === null) return true; return Date.now() < item.expiresAt; } ``` ## Stap 3: Implementatie van Type-Veilige In-Memory Cache ```typescript class InMemoryCache<K, V> implements Cache<K, V> { private storage: Map<K, CacheItem<V>>; private defaultTTL: number | null; constructor(defaultTTL?: number) { this.storage = new Map(); this.defaultTTL = defaultTTL || null; } // Set een waarde in de cache met optionele TTL set(key: K, value: V, ttl?: number): void { const actualTTL = ttl ?? this.defaultTTL; const expiresAt = actualTTL ? Date.now() + actualTTL : null; this.storage.set(key, { value, expiresAt }); } // Haal een waarde op met typeveiligheid get(key: K): V | undefined { const item = this.storage.get(key); if (!isValidCacheItem(item)) { // Verwijder verlopen items automatisch this.storage.delete(key); return undefined; } return item.value; } // Type-veilige check of key bestaat has(key: K): boolean { const item = this.storage.get(key); return isValidCacheItem(item); } // Verwijder een item delete(key: K): boolean { return this.storage.delete(key); } // Leeg de cache clear(): void { this.storage.clear(); } // Aantal geldige items size(): number { return Array.from(this.storage.values()) .filter(isValidCacheItem) .length; } // Alle geldige keys keys(): K[] { return Array.from(this.storage.entries()) .filter(([_, item]) => isValidCacheItem(item)) .map(([key]) => key); } // Alle geldige values values(): V[] { return Array.from(this.storage.values()) .filter(isValidCacheItem) .map(item => item.value); } // Extra methoden voor geavanceerd gebruik // Verleng TTL van een bestaand item renewTTL(key: K, ttl: number): boolean { const item = this.storage.get(key); if (!isValidCacheItem(item)) { return false; } item.expiresAt = Date.now() + ttl; return true; } // Verwijder alle verlopen items cleanup(): number { let removedCount = 0; for (const [key, item] of this.storage.entries()) { if (!isValidCacheItem(item)) { this.storage.delete(key); removedCount++; } } return removedCount; } // Cache statistieken getStats(): { total: number; valid: number; expired: number } { const total = this.storage.size; const valid = this.size(); return { total, valid, expired: total - valid }; } } ``` ## Stap 4: Gespecialiseerde Cache Klassen ```typescript // Cache voor API responses met automatische serialisatie class ApiResponseCache<T> extends InMemoryCache<string, T> { constructor(defaultTTL?: number) { super(defaultTTL); } // Serialiseer complexe keys voor API endpoints setWithParams(endpoint: string, params: Record<string, unknown>, value: T, ttl?: number): void { const key = this.serializeKey(endpoint, params); this.set(key, value, ttl); } getWithParams(endpoint: string, params: Record<string, unknown>): T | undefined { const key = this.serializeKey(endpoint, params); return this.get(key); } private serializeKey(endpoint: string, params: Record<string, unknown>): string { const sortedParams = Object.keys(params) .sort() .reduce((acc, key) => { acc[key] = params[key]; return acc; }, {} as Record<string, unknown>); return `${endpoint}:${JSON.stringify(sortedParams)}`; } } // Cache voor functie resultaten (memoization) class MemoizationCache<Args extends any[], Result> { private cache: InMemoryCache<string, Result>; constructor(defaultTTL?: number) { this.cache = new InMemoryCache(defaultTTL); } memoize(fn: (...args: Args) => Result, ...args: Args): Result { const key = JSON.stringify(args); const cached = this.cache.get(key); if (cached !== undefined) { return cached; } const result = fn(...args); this.cache.set(key, result); return result; } clear(): void { this.cache.clear(); } } ``` ## Stap 5: Gebruiksvoorbeelden ```typescript // Basis gebruik interface User { id: number; name: string; email: string; } // Cache voor gebruikers met 5 minuten TTL const userCache = new InMemoryCache<string, User>(5 * 60 * 1000); // Type-veilig gebruik const user: User = { id: 1, name: "Jan Jansen", email: "jan@voorbeeld.nl" }; userCache.set("user:1", user); userCache.set("user:2", user, 10 * 60 * 1000); // 10 minuten TTL // Type inference werkt perfect const cachedUser = userCache.get("user:1"); if (cachedUser) { console.log(cachedUser.name); // TypeScript weet dat dit een string is } // API response cache interface ApiResponse { data: User[]; pagination: { page: number; total: number }; } const apiCache = new ApiResponseCache<ApiResponse>(30 * 60 * 1000); apiCache.setWithParams( "/api/users", { page: 1, limit: 10 }, { data: [user], pagination: { page: 1, total: 1 } } ); const response = apiCache.getWithParams("/api/users", { page: 1, limit: 10 }); // Memoization voor dure berekeningen const expensiveCalculation = (a: number, b: number): number => { console.log('Calculating...'); return a * b + Math.sqrt(a) * Math.sin(b); }; const calculationCache = new MemoizationCache<[number, number], number>(); // Eerste keer wordt berekend const result1 = calculationCache.memoize(expensiveCalculation, 5, 10); // Tweede keer wordt uit cache gehaald const result2 = calculationCache.memoize(expensiveCalculation, 5, 10); ``` ## Stap 6: Beste Praktijken en Patronen ```typescript // 1. Factory pattern voor cache creatie class CacheFactory { static createUserCache(): InMemoryCache<string, User> { return new InMemoryCache(15 * 60 * 1000); // 15 minuten } static createSessionCache(): InMemoryCache<string, unknown> { return new InMemoryCache(24 * 60 * 60 * 1000); // 24 uur } } // 2. Decorator voor automatische caching function cacheResult<K, V>(cache: InMemoryCache<K, V>, key: K, ttl?: number) { return function (target: any, propertyName: string, descriptor: PropertyDescriptor) { const method = descriptor.value; descriptor.value = function (...args: any[]) { const cached = cache.get(key); if (cached !== undefined) { return cached; } const result = method.apply(this, args); cache.set(key, result, ttl); return result; }; return descriptor; }; } // 3. Gebruik met async functies class AsyncCache<K, V> extends InMemoryCache<K, Promise<V>> { async getOrSet(key: K, factory: () => Promise<V>, ttl?: number): Promise<V> { const cached = this.get(key); if (cached) { return cached; } const promise = factory(); this.set(key, promise, ttl); try { const result = await promise; return result; } catch (error) { // Verwijder de cache bij errors this.delete(key); throw error; } } } ``` ## Belangrijkste Type-Veiligheid Kenmerken: 1. **Generieke Types**: `K` voor keys, `V` voor values 2. **Type Inference**: Automatische type-detectie bij gebruik 3. **Type Guards**: `isValidCacheItem` voor runtime type checking 4. **Compile-time Safety**: TypeScript voorkomt type fouten tijdens ontwikkeling 5. **Consistente Interfaces**: Duidelijke contracten tussen componenten ## Aanbevolen Praktijken: 1. **Stel realistische TTLs** in op basis van je use case 2. **Implementeer cleanup mechanismen** voor verlopen items 3. **Gebruik betekenisvolle keys** die eenvoudig te serialiseren zijn 4. **Monitor cache performance** met de statistieken methoden 5. **Overweeg memory limits** voor productie gebruik 6. **Test je cache grondig** met verschillende scenario's Deze implementatie biedt een robuuste, type-veilige foundation die je kunt uitbreiden op basis van je specifieke requirements!