memo
The memo utility creates a memoized version of a function that caches its results based on the provided arguments. It is highly configurable, featuring support for Time-To-Live (TTL) expiration and a maximum cache size with LRU (Least Recently Used) eviction.
Implementation
View Source Code
ts
/** biome-ignore-all lint/suspicious/noExplicitAny: - */
import type { Fn } from '../types';
// #region MemoizeOptions
type MemoizeOptions<T extends Fn> = {
ttl?: number; // Time-to-live in milliseconds
maxSize?: number; // Maximum number of items in cache
resolver?: (...args: Parameters<T>) => string; // Custom key generator
};
// #endregion MemoizeOptions
type CacheEntry<T extends Fn> = {
value: ReturnType<T>;
timestamp: number;
};
/**
* Creates a function that memorizes the result of the provided function.
* Supports expiration (TTL) and limited cache size (LRU).
*
* @example
* ```ts
* const add = (x, y) => x + y;
* const memoizedAdd = memo(add, { ttl: 5000, maxSize: 10 });
*
* memoizedAdd(1, 2); // 3 (caches the result)
* memoizedAdd(1, 2); // 3 (from cache)
* ```
*
* @param fn - The function to memorize.
* @param options - Memoization options.
* @param [options.ttl] - (optional) time-to-live (TTL) for cache expiration (in milliseconds).
* @param [options.maxSize] - (optional) maximum cache size (LRU eviction).
* @param [options.resolver] - (optional) custom function to resolve the cache key.
*
* @returns A new function that memorizes the input function.
*/
export function memo<T extends Fn>(
fn: T,
{ ttl, maxSize, resolver }: MemoizeOptions<T> = {},
): (...args: Parameters<T>) => ReturnType<T> {
const cache = new Map<string, CacheEntry<T>>();
// biome-ignore lint/complexity/noExcessiveCognitiveComplexity: -
const keyGen = (args: Parameters<T>): string => {
if (resolver) return resolver(...args);
if (args.length === 0) return '__empty__';
if (args.length === 1) {
const arg = args[0];
const argType = typeof arg;
if (argType === 'string' || argType === 'number' || argType === 'boolean') {
return `${argType}:${arg}`;
}
if (arg === null) return 'null';
if (arg === undefined) return 'undefined';
}
return JSON.stringify(args);
};
return (...args: Parameters<T>): ReturnType<T> => {
const key = keyGen(args);
const now = Date.now();
const cached = cache.get(key);
if (cached && (!ttl || now - cached.timestamp < ttl)) {
cache.delete(key);
cache.set(key, cached); // Move to end (most recently used)
return cached.value;
}
const result = fn(...args);
cache.set(key, { timestamp: now, value: result });
if (maxSize && cache.size > maxSize) {
cache.delete(cache.keys().next().value!); // Remove least recently used
}
return result;
};
}Features
- Isomorphic: Works in both Browser and Node.js.
- Smart Caching: Efficiently stores and retrieves results for pure functions.
- Cache Management: Prevent memory leaks with
maxSizeandttloptions. - LRU Eviction: Automatically removes the oldest entries when the cache limit is reached.
- Type-safe: Properly preserves the original function's signature and return type.
API
Type Definitions
ts
type MemoizeOptions<T extends Fn> = {
ttl?: number; // Time-to-live in milliseconds
maxSize?: number; // Maximum number of items in cache
resolver?: (...args: Parameters<T>) => string; // Custom key generator
};ts
function memo<T extends (...args: any[]) => any>(fn: T, options?: MemoizeOptions<T>): T;Parameters
fn: The function to memoize.options: Optional configuration:ttl: Time in milliseconds before a cached result expires.maxSize: Maximum number of entries to keep in the cache.
Returns
- A new function that caches results.
Examples
Basic Memoization
ts
import { memo } from '@vielzeug/toolkit';
const heavyCalculation = (n: number) => {
console.log('Calculating...');
return n * n;
};
const cachedCalc = memo(heavyCalculation);
cachedCalc(5); // Logs 'Calculating...', returns 25
cachedCalc(5); // Returns 25 immediately (from cache)With Expiration (TTL)
ts
import { memo } from '@vielzeug/toolkit';
// Cache results for only 1 minute
const getStats = memo(fetchStats, { ttl: 60000 });Limiting Memory Usage
ts
import { memo } from '@vielzeug/toolkit';
// Keep only the last 100 results
const formatData = memo(formatter, { maxSize: 100 });Implementation Notes
- Performance-optimized using a standard
Mapfor the cache. - The cache key is generated based on the string representation of all arguments.
- Throws
TypeErroriffnis not a function.