cachedts is a lightweight utility for function-level memoization of object methods in TypeScript.
It wraps an object and automatically caches the results of its functions based on input arguments.
Perfect for avoiding repeated computations, expensive API calls, or just improving performance with minimal setup.
- Zero-dependency
- Memoizes function results by arguments
- Optional TTL-based expiration
- LRU cache eviction with configurable
maxSize - Per-function or global settings
- Debug logging
- Custom cache key generation
import { cached } from "cachedts";
const mathApi = {
slowSquare(x: number) {
console.log("Calculating...");
return x * x;
},
};
const cachedMath = cached(mathApi);
cachedMath.slowSquare(5); // logs "Calculating..." and returns 25
cachedMath.slowSquare(5); // returns 25 instantly (from cache)const cachedApi = cached(apiObject, {
debug: true, // Log cache hits/misses
settings: {
enabled: true,
ttl: 5000, // Cache expires after 5 seconds
maxSize: 100, // Keep at most 100 entries per function (LRU eviction)
},
overrides: {
// Per-method override
expensiveMethod: { ttl: 10000 },
},
getCacheKey(methodName, args) {
return `${methodName}-${JSON.stringify(args)}`;
},
});| Option | Type | Description |
|---|---|---|
cache |
Map |
Provide a shared cache instance |
debug |
boolean |
Enable logging for cache hits/misses |
settings |
{ enabled?: boolean; ttl?: number; maxSize?: number } |
Global cache settings |
overrides |
Partial<Record<keyof TApi, CacheSettings>> |
Per-method cache control |
getCacheKey |
(methodName, args) => string | symbol |
Custom key generator for cache entries |
const api = {
greet(name: string) {
return `Hello, ${name}`;
},
};
const cachedApi = cached(api, {
settings: { ttl: 1000 },
});
cachedApi.greet("Alice"); // cache miss
setTimeout(() => {
cachedApi.greet("Alice"); // cache hit
}, 500);
setTimeout(() => {
cachedApi.greet("Alice"); // cache expired, miss
}, 1500);Limit the number of cached entries per function with maxSize. When the limit is exceeded, the least recently used entry is evicted.
const cachedApi = cached(api, {
settings: { maxSize: 2 },
});
cachedApi.greet("Alice"); // cached
cachedApi.greet("Bob"); // cached
cachedApi.greet("Eve"); // cached, "Alice" evicted (least recently used)maxSize can be combined with ttl and set per-method via overrides.
When ttl is set, expired entries are automatically pruned from a function's cache on every cache miss. This ensures expired entries don't accumulate and, when combined with maxSize, prevents LRU eviction from evicting valid entries over expired ones. Cache hits do not trigger pruning.
For bulk cleanup across all functions, use prune():
import { cached, prune } from "cachedts";
const cachedApi = cached(api, { settings: { ttl: 5000 } });
// Remove all expired entries across all functions
prune(cachedApi);Pruning only sweeps the cache of the function being called, not all functions. Both automatic and manual pruning respect per-function TTL overrides.
You can clear cached results using the invalidate function. It allows three levels of granularity:
import { invalidate } from "cachedts";
invalidate(cachedApi); // Clears all cached entries for all methodsinvalidate(cachedApi, "getParams"); // Clears all cache entries for `getParams`invalidate(cachedApi, "getParams", "user123", 42); // Clears cache entry for `getParams("user123", 42)`This is useful when the underlying data changes and a fresh fetch is needed for specific arguments.
- No effect is applied if the method is not a function or has no cached entries.
- Cache key computation uses the same mechanism as the one used by
cached(), including customgetCacheKeyif provided.
By default, cache keys are generated based on the function name and serialized arguments.
To customize:
const cachedApi = cached(api, {
getCacheKey(methodName, args) {
if (methodName === "fetchUser") {
return args[0]; // Use user ID directly
}
},
});You can introspect the wrapped object via the cacheStateKey:
import { cacheStateKey } from "cachedts";
const state = cachedApi[cacheStateKey];
console.log(state.cache); // underlying MapOr by using the getCacheState function:
import { getCacheState } from "cachedts";
const state = getCacheState(cachedApi);
console.log(state.cache); // nderlying MapDisable globally or per-method:
cached(api, {
settings: { enabled: false }, // globally off
overrides: {
compute: { enabled: true }, // override for `compute` method
},
});npm install cachedtsMIT