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
...
tags
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!

