Typed Cache Interface
The TypedCacheInterface<T>
provides a type-safe wrapper around RunCache operations for a specific type. This interface ensures compile-time type checking and better developer experience when working with consistently typed cache entries.
Class: TypedCacheInterface
Created using RunCache.createTypedCache<T>()
, this interface provides all standard cache operations with strong typing for the specified type T
.
Type Parameters
T
- The type of values that will be cached in this interface
Cache Operations
set(options)
set(options)
Sets a cache entry with type-safe value validation.
Parameters:
options
:Object
- Cache entry optionskey
:string
- Unique identifier for the cache entryvalue?
:T
- Typed value to cache (required if no sourceFn)ttl?
:number
- Time-to-live in millisecondsautoRefetch?
:boolean
- Automatically refetch on expiry (requires ttl and sourceFn)sourceFn?
:() => T | Promise<T>
- Function to generate typed cache value (required if no value)tags?
:string[]
- Array of tags for tag-based invalidationdependencies?
:string[]
- Array of cache keys this entry depends onmetadata?
:any
- Custom metadata to associate with the entryvalidator?
:TypeValidator<T>
- Optional type validator for runtime checking
Returns: Promise<boolean>
- Returns true
if the value was successfully set
Example:
interface User {
id: number;
name: string;
email: string;
profile?: {
avatar?: string;
bio?: string;
};
}
const userCache = RunCache.createTypedCache<User>();
// Type-safe setting
await userCache.set({
key: 'user:123',
value: {
id: 123,
name: 'John Doe',
email: 'john@example.com',
profile: {
avatar: 'https://example.com/avatar.jpg',
bio: 'Software developer'
}
},
ttl: 3600000 // 1 hour
});
// With source function
await userCache.set({
key: 'user:456',
sourceFn: async (): Promise<User> => {
const response = await fetch('/api/users/456');
return response.json();
},
ttl: 1800000, // 30 minutes
autoRefetch: true
});
// TypeScript will catch type errors at compile time
// await userCache.set({
// key: 'user:789',
// value: { id: '789', name: 'Jane' } // ❌ Type error: id should be number
// });
get(key)
get(key)
Retrieves a typed cache entry by key or pattern.
Parameters:
key
:string
- The key to retrieve, or a pattern with wildcards
Returns: Promise<T | T[] | undefined>
If
key
is a specific key, returns the typed value or undefined if not foundIf
key
is a pattern, returns an array of matching typed values
Example:
// Get a specific user
const user = await userCache.get('user:123');
if (user) {
console.log(user.name); // TypeScript knows this is a string
console.log(user.id); // TypeScript knows this is a number
console.log(user.profile?.bio); // Optional chaining with type safety
}
// Get multiple users with pattern matching
const allUsers = await userCache.get('user:*');
if (Array.isArray(allUsers)) {
allUsers.forEach(u => {
console.log(`${u.name} (${u.email})`); // Full type safety
});
}
has(key)
has(key)
Checks if a valid (non-expired) cache entry exists.
Parameters:
key
:string
- The key to check, or a pattern with wildcards
Returns: Promise<boolean>
Example:
const exists = await userCache.has('user:123');
if (exists) {
console.log('User is cached');
}
// Check if any users are cached
const hasAnyUsers = await userCache.has('user:*');
delete(key)
delete(key)
Removes a cache entry or entries matching a pattern.
Parameters:
key
:string
- The key to delete, or a pattern with wildcards
Returns: Promise<void>
Example:
// Delete a specific user
await userCache.delete('user:123');
// Delete all users
await userCache.delete('user:*');
refetch(key, options?)
refetch(key, options?)
Manually refreshes a cache entry or entries matching a pattern.
Parameters:
key
:string
- The key to refresh, or a pattern with wildcardsoptions?
:Object
- Optional refresh optionsmetadata?
:any
- Custom metadata to include in refetch events
Returns: Promise<void>
Example:
// Refresh a specific user
await userCache.refetch('user:123');
// Refresh all users
await userCache.refetch('user:*');
// Refresh with metadata
await userCache.refetch('user:123', {
metadata: { reason: 'manual-refresh', userId: 123 }
});
Event System Integration
Typed cache interfaces work seamlessly with the event system, providing typed event parameters.
Event Registration
// Type-safe event handlers
userCache.onExpiry((event) => {
console.log(`User ${event.value.name} expired from cache`);
// event.value is typed as User
});
userCache.onRefetch((event) => {
console.log(`User cache refreshed: ${event.key}`);
});
userCache.onRefetchFailure((event) => {
console.error(`Failed to refresh user: ${event.error.message}`);
});
Advanced Usage Patterns
Repository Pattern
Use typed cache interfaces to implement repository patterns:
class UserRepository {
private cache = RunCache.createTypedCache<User>();
async findById(id: number): Promise<User | undefined> {
return this.cache.get(`user:${id}`);
}
async save(user: User, ttl?: number): Promise<void> {
await this.cache.set({
key: `user:${user.id}`,
value: user,
ttl: ttl || 3600000, // Default 1 hour
tags: ['user', `user:${user.id}`]
});
}
async findByEmail(email: string): Promise<User | undefined> {
const users = await this.cache.get('user:*');
if (Array.isArray(users)) {
return users.find(u => u.email === email);
}
return undefined;
}
async invalidateUser(id: number): Promise<void> {
await this.cache.delete(`user:${id}`);
}
async refreshUser(id: number): Promise<void> {
await this.cache.refetch(`user:${id}`);
}
}
// Usage
const userRepo = new UserRepository();
const user = await userRepo.findById(123);
if (user) {
console.log(user.name); // Fully typed
}
Service Layer Integration
Integrate typed caches with service layers:
interface Product {
id: number;
name: string;
price: number;
category: string;
inStock: boolean;
}
class ProductService {
private cache = RunCache.createTypedCache<Product>();
async getProduct(id: number): Promise<Product | null> {
// Try cache first
const cached = await this.cache.get(`product:${id}`);
if (cached) {
return cached;
}
// Fetch from database/API
const product = await this.fetchProductFromDB(id);
if (product) {
// Cache with 30-minute TTL
await this.cache.set({
key: `product:${id}`,
value: product,
ttl: 1800000,
tags: ['product', `category:${product.category}`]
});
}
return product;
}
async updateProduct(product: Product): Promise<void> {
await this.updateProductInDB(product);
// Update cache
await this.cache.set({
key: `product:${product.id}`,
value: product,
ttl: 1800000,
tags: ['product', `category:${product.category}`]
});
}
async invalidateCategory(category: string): Promise<void> {
// This would require tag invalidation on the main RunCache
await RunCache.invalidateByTag(`category:${category}`);
}
private async fetchProductFromDB(id: number): Promise<Product | null> {
// Database fetch logic
return null; // Placeholder
}
private async updateProductInDB(product: Product): Promise<void> {
// Database update logic
}
}
Multi-Type Cache Management
Manage multiple typed caches in a single class:
interface Order {
id: number;
userId: number;
products: Product[];
total: number;
status: 'pending' | 'processing' | 'shipped' | 'delivered';
}
class CacheManager {
private userCache = RunCache.createTypedCache<User>();
private productCache = RunCache.createTypedCache<Product>();
private orderCache = RunCache.createTypedCache<Order>();
// User operations
async getUser(id: number): Promise<User | undefined> {
return this.userCache.get(`user:${id}`);
}
async setUser(user: User): Promise<void> {
await this.userCache.set({
key: `user:${user.id}`,
value: user,
ttl: 3600000,
tags: ['user']
});
}
// Product operations
async getProduct(id: number): Promise<Product | undefined> {
return this.productCache.get(`product:${id}`);
}
async setProduct(product: Product): Promise<void> {
await this.productCache.set({
key: `product:${product.id}`,
value: product,
ttl: 1800000,
tags: ['product', `category:${product.category}`]
});
}
// Order operations
async getOrder(id: number): Promise<Order | undefined> {
return this.orderCache.get(`order:${id}`);
}
async setOrder(order: Order): Promise<void> {
await this.orderCache.set({
key: `order:${order.id}`,
value: order,
ttl: 7200000, // 2 hours
tags: ['order', `user:${order.userId}`],
dependencies: [`user:${order.userId}`]
});
}
// Cross-type operations
async getUserOrders(userId: number): Promise<Order[]> {
const orders = await this.orderCache.get('order:*');
if (Array.isArray(orders)) {
return orders.filter(order => order.userId === userId);
}
return [];
}
async invalidateUserData(userId: number): Promise<void> {
await this.userCache.delete(`user:${userId}`);
await RunCache.invalidateByTag(`user:${userId}`);
}
}
Type Safety Benefits
Compile-Time Type Checking
interface StrictUser {
id: number;
name: string;
email: string;
createdAt: Date;
}
const strictUserCache = RunCache.createTypedCache<StrictUser>();
// ✅ Valid usage
await strictUserCache.set({
key: 'user:1',
value: {
id: 1,
name: 'John',
email: 'john@example.com',
createdAt: new Date()
}
});
// ❌ Compile-time errors
/*
await strictUserCache.set({
key: 'user:2',
value: {
id: '2', // ❌ Type error: string not assignable to number
name: 'Jane',
email: 'jane@example.com'
// ❌ Type error: missing required property 'createdAt'
}
});
*/
Runtime Type Validation
Combine with type validators for runtime checking:
import { ValidatorUtils, NumberValidator, StringValidator } from 'run-cache';
const userValidator = ValidatorUtils.object({
id: NumberValidator,
name: StringValidator,
email: ValidatorUtils.string(email => email.includes('@')),
createdAt: ValidatorUtils.instanceOf(Date)
});
await strictUserCache.set({
key: 'user:1',
value: userData,
validator: userValidator, // Runtime validation
validateOnSet: true
});
Performance Considerations
Typed cache interfaces have minimal overhead compared to the main RunCache:
Type Checking: Zero runtime cost (compile-time only)
Memory Usage: Same as main RunCache (just a wrapper)
Serialization: Same performance characteristics
Method Calls: Minimal indirection overhead (< 1ms)
Best Practices
Use for Consistent Types: Create typed interfaces for types you cache frequently
Repository Pattern: Combine with repository patterns for clean architecture
Service Integration: Integrate with service layers for business logic separation
Type Validation: Use runtime validators for critical data integrity
Event Handling: Leverage typed events for better debugging and monitoring
Next Steps
Learn about Serialization Adapters for complex types
Explore Type Validation for runtime checking
See Best Practices for typed caching patterns
Check the main RunCache API for additional methods
Last updated