Blazing fast memoization library for JavaScript with comprehensive configuration options and React support
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
Utility functions for working with memoized functions, composition, and type checking in the moize ecosystem.
Check if a value is a memoized function created by moize.
/**
* Type guard to check if a value is a memoized function
* @param value The value to test
* @returns True if the value is a memoized function, false otherwise
*/
isMoized(value: any): value is Moized;Usage Examples:
import moize from "moize";
const regularFunction = (x: number) => x * 2;
const memoizedFunction = moize(regularFunction);
// Type checking
console.log(moize.isMoized(regularFunction)); // false
console.log(moize.isMoized(memoizedFunction)); // true
console.log(moize.isMoized("not a function")); // false
console.log(moize.isMoized(null)); // false
// Use in conditional logic
function processFunction(fn: any) {
if (moize.isMoized(fn)) {
console.log('Working with memoized function');
console.log('Cache size:', Array.from(fn.keys()).length);
console.log('Original function name:', fn.originalFunction.name);
// Access memoized function methods
fn.clear();
console.log('Cache cleared');
} else if (typeof fn === 'function') {
console.log('Working with regular function');
console.log('Function name:', fn.name);
} else {
console.log('Not a function');
}
}
processFunction(regularFunction); // "Working with regular function"
processFunction(memoizedFunction); // "Working with memoized function"
// Type-safe utility function
function getMemoizedFunctionStats(fn: unknown): { calls: number; hits: number } | null {
if (moize.isMoized(fn)) {
return fn.getStats();
}
return null;
}
const stats = getMemoizedFunctionStats(memoizedFunction);
console.log(stats); // { calls: 0, hits: 0 }Compose multiple moizers to create complex memoization strategies.
/**
* Compose multiple moizers into a single moizer
* @param moizers The moizers or moize instances to compose
* @returns A composed moizer that applies all configurations
*/
compose(...moizers: Array<Moize | Moizer>): Moizer;Usage Examples:
import moize from "moize";
// Define individual moization strategies
const withCaching = moize.maxSize(50);
const withTTL = moize.maxAge(30000); // 30 seconds
const withProfiling = moize.profile('composed-functions');
const withDeepEquality = moize.deep;
// Compose strategies
const composedMemoizer = moize.compose(
withCaching,
withTTL,
withProfiling,
withDeepEquality
);
const expensiveOperation = (data: { items: any[]; config: any }) => {
return data.items
.filter(item => item.active)
.map(item => ({ ...item, processed: true, timestamp: Date.now() }));
};
// Apply composed memoization
const memoizedOperation = composedMemoizer(expensiveOperation);
// The function now has all composed behaviors:
// - Max 50 cache entries
// - 30 second TTL
// - Profiled under 'composed-functions'
// - Uses deep equality for comparison
console.log(memoizedOperation.options);
// Contains all merged options from composed moizers
// Complex composition example
const apiMemoizer = moize.compose(
moize.promise, // Handle async functions
moize.serialize, // Serialize complex arguments
moize.maxAge(60000), // 1 minute TTL
moize.maxSize(100), // Up to 100 cached responses
moize.profile('api-calls') // Profile API performance
);
const fetchUserData = async (userId: string, includePreferences: boolean) => {
const response = await fetch(`/api/users/${userId}?prefs=${includePreferences}`);
return response.json();
};
const memoizedFetchUserData = apiMemoizer(fetchUserData);
// Database operation composition
const dbMemoizer = moize.compose(
moize.promise,
moize.deep, // Deep comparison for query objects
moize.maxAge(120000), // 2 minute TTL
moize.transformArgs((key) => { // Normalize query objects
return key.map(arg => {
if (arg && typeof arg === 'object' && 'query' in arg) {
// Sort query keys for consistent caching
const sortedQuery = Object.keys(arg.query)
.sort()
.reduce((result, key) => {
result[key] = arg.query[key];
return result;
}, {} as any);
return { ...arg, query: sortedQuery };
}
return arg;
});
}),
moize.profile('database')
);
const queryDatabase = async (query: { table: string; where: any; limit?: number }) => {
// Database query implementation
return { results: [], count: 0 };
};
const memoizedDbQuery = dbMemoizer(queryDatabase);The order of composition matters for certain options, with later moizers taking precedence.
import moize from "moize";
const func = (x: number) => x * x;
// Order matters for conflicting options
const composed1 = moize.compose(
moize.maxSize(10), // This will be overridden
moize.maxSize(20) // This takes precedence
);
const composed2 = moize.compose(
moize.maxSize(20), // This will be overridden
moize.maxSize(10) // This takes precedence
);
const memoized1 = composed1(func);
const memoized2 = composed2(func);
console.log(memoized1.options.maxSize); // 20
console.log(memoized2.options.maxSize); // 10
// Non-conflicting options are merged
const mergedComposition = moize.compose(
moize.maxSize(15),
moize.maxAge(5000),
moize.deep,
moize.profile('merged')
);
const mergedMemoized = mergedComposition(func);
console.log(mergedMemoized.options);
// {
// maxSize: 15,
// maxAge: 5000,
// isDeepEqual: true,
// profileName: 'merged',
// // ... other options
// }Common composition patterns for different use cases.
import moize from "moize";
// API call memoization pattern
const createApiMemoizer = (cacheDuration: number, cacheSize: number, profileName: string) => {
return moize.compose(
moize.promise,
moize.serialize,
moize.maxAge(cacheDuration, {
updateExpire: true, // Refresh TTL on cache hits
onExpire: (key) => console.log(`API cache expired for:`, key)
}),
moize.maxSize(cacheSize),
moize.profile(profileName)
);
};
// Usage
const shortTermApiMemoizer = createApiMemoizer(30000, 50, 'short-api'); // 30s
const longTermApiMemoizer = createApiMemoizer(300000, 20, 'long-api'); // 5min
// Computation memoization pattern
const createComputationMemoizer = (isHeavyComputation: boolean) => {
const baseComposition = [
moize.deep, // Usually need deep equality for complex data
moize.profile('computations')
];
if (isHeavyComputation) {
return moize.compose(
...baseComposition,
moize.infinite, // No size limit for expensive computations
moize.transformArgs((key) => {
// Normalize computation arguments
return key.map(arg => {
if (Array.isArray(arg)) {
return [...arg].sort(); // Sort arrays for consistent caching
}
return arg;
});
})
);
} else {
return moize.compose(
...baseComposition,
moize.maxSize(100),
moize.maxAge(60000) // 1 minute for lighter computations
);
}
};
// React component memoization pattern
const createReactMemoizer = (useDeepComparison: boolean = false) => {
const composition = [moize.react, moize.maxSize(20)];
if (useDeepComparison) {
composition.push(moize.deep);
} else {
composition.push(moize.shallow);
}
return moize.compose(...composition);
};
// Development vs Production memoization
const createEnvironmentMemoizer = (isDevelopment: boolean) => {
const baseComposition = [
moize.maxSize(50),
moize.maxAge(60000)
];
if (isDevelopment) {
// More verbose profiling and smaller cache in development
return moize.compose(
...baseComposition,
moize.profile('dev-functions'),
moize.collectStats(true)
);
} else {
// Optimized for production
return moize.compose(
...baseComposition,
moize.serialize // Better cache key consistency in production
);
}
};