0
# Argument Transformation
1
2
Methods for transforming and manipulating function arguments before caching, enabling advanced cache key customization and conditional cache updates.
3
4
## Capabilities
5
6
### Argument Transformation
7
8
Transform arguments before they are used to generate cache keys, allowing for normalization and custom cache key generation.
9
10
```typescript { .api }
11
/**
12
* Transform arguments before caching
13
* @param transformer Function to transform the argument array (cache key)
14
* @returns Moizer with argument transformation
15
*/
16
transformArgs<Transformer extends TransformKey>(
17
transformer: Transformer
18
): Moizer<{ transformArgs: Transformer }>;
19
20
type TransformKey = (key: Key) => Key;
21
type Key<Arg extends any = any> = Arg[];
22
```
23
24
**Usage Examples:**
25
26
```typescript
27
import moize from "moize";
28
29
// Normalize string arguments
30
const normalizeStrings = (key: any[]) => {
31
return key.map(arg =>
32
typeof arg === 'string' ? arg.toLowerCase().trim() : arg
33
);
34
};
35
36
const processText = (text: string, options: { uppercase: boolean }) => {
37
return options.uppercase ? text.toUpperCase() : text.toLowerCase();
38
};
39
40
const normalizedMemoized = moize.transformArgs(normalizeStrings)(processText);
41
42
console.log(normalizedMemoized(" Hello ", { uppercase: true })); // Computed
43
console.log(normalizedMemoized("hello", { uppercase: true })); // Cached (normalized match)
44
45
// Sort array arguments for order-independent caching
46
const sortArrayArgs = (key: any[]) => {
47
return key.map(arg => {
48
if (Array.isArray(arg)) {
49
return [...arg].sort();
50
}
51
return arg;
52
});
53
};
54
55
const sumArray = (numbers: number[]) => numbers.reduce((a, b) => a + b, 0);
56
const orderIndependentSum = moize.transformArgs(sortArrayArgs)(sumArray);
57
58
console.log(orderIndependentSum([3, 1, 2])); // 6 (computed)
59
console.log(orderIndependentSum([1, 2, 3])); // 6 (cached - same sorted values)
60
61
// Extract specific properties for cache key
62
const extractUserKey = (key: any[]) => {
63
return key.map(arg => {
64
if (arg && typeof arg === 'object' && 'id' in arg && 'role' in arg) {
65
return { id: arg.id, role: arg.role }; // Only use id and role for caching
66
}
67
return arg;
68
});
69
};
70
71
const processUser = (user: {
72
id: string;
73
name: string;
74
email: string;
75
role: string;
76
lastLogin?: Date;
77
}) => {
78
return `${user.role.toUpperCase()}: ${user.name}`;
79
};
80
81
const userMemoized = moize.transformArgs(extractUserKey)(processUser);
82
83
const user1 = { id: "1", name: "Alice", email: "alice@example.com", role: "admin", lastLogin: new Date() };
84
const user2 = { id: "1", name: "Alice", email: "alice@newdomain.com", role: "admin" }; // Different email, no lastLogin
85
86
console.log(userMemoized(user1)); // Computed
87
console.log(userMemoized(user2)); // Cached (same id and role)
88
```
89
90
### Conditional Cache Updates
91
92
Control when cache entries should be updated based on the current cache key.
93
94
```typescript { .api }
95
/**
96
* Conditionally update cache based on key analysis
97
* @param updateCacheForKey Function that determines when to update cache
98
* @returns Moizer with conditional cache updating
99
*/
100
updateCacheForKey<UpdateWhen extends UpdateCacheForKey>(
101
updateCacheForKey: UpdateWhen
102
): Moizer<{ updateCacheForKey: UpdateWhen }>;
103
104
type UpdateCacheForKey = (key: Key) => boolean;
105
```
106
107
**Usage Examples:**
108
109
```typescript
110
import moize from "moize";
111
112
// Update cache only for specific conditions
113
const shouldUpdateCache = (key: any[]) => {
114
const [data, options] = key;
115
116
// Always update if force refresh is requested
117
if (options && options.forceRefresh) {
118
return true;
119
}
120
121
// Update if data is marked as stale
122
if (data && data.lastModified) {
123
const fiveMinutesAgo = Date.now() - 5 * 60 * 1000;
124
return data.lastModified > fiveMinutesAgo;
125
}
126
127
return false; // Use cached version otherwise
128
};
129
130
const processData = (
131
data: { content: any; lastModified?: number },
132
options: { forceRefresh?: boolean } = {}
133
) => {
134
console.log('Processing data...');
135
return { processed: data.content, timestamp: Date.now() };
136
};
137
138
const conditionalMemoized = moize.updateCacheForKey(shouldUpdateCache)(processData);
139
140
const oldData = { content: "test", lastModified: Date.now() - 10 * 60 * 1000 }; // 10 min ago
141
const freshData = { content: "test", lastModified: Date.now() }; // Now
142
143
console.log(conditionalMemoized(oldData)); // Uses cache if available
144
console.log(conditionalMemoized(freshData)); // Always recomputes (fresh data)
145
console.log(conditionalMemoized(oldData, { forceRefresh: true })); // Always recomputes (forced)
146
147
// Version-based cache updating
148
const versionBasedUpdate = (key: any[]) => {
149
const [resource] = key;
150
151
if (!resource || typeof resource.version !== 'number') {
152
return true; // Update if no version info
153
}
154
155
// Update if version is different from what might be cached
156
return true; // In practice, you'd compare with cached version
157
};
158
159
const fetchResource = (resource: { id: string; version: number }) => {
160
console.log(`Fetching resource ${resource.id} v${resource.version}`);
161
return { data: `Resource ${resource.id}`, version: resource.version };
162
};
163
164
const versionMemoized = moize.updateCacheForKey(versionBasedUpdate)(fetchResource);
165
166
// Time-based cache invalidation
167
const timeBasedUpdate = (key: any[]) => {
168
const [, options] = key;
169
170
if (options && options.timestamp) {
171
const thirtySecondsAgo = Date.now() - 30 * 1000;
172
return options.timestamp > thirtySecondsAgo; // Update if timestamp is recent
173
}
174
175
return false;
176
};
177
178
const timestampMemoized = moize.updateCacheForKey(timeBasedUpdate)(processData);
179
```
180
181
### Combining Transformation Methods
182
183
Argument transformation methods can be combined with other moize features for sophisticated caching strategies.
184
185
```typescript
186
import moize from "moize";
187
188
// Normalize and extract key properties
189
const normalizeAndExtract = (key: any[]) => {
190
return key.map(arg => {
191
if (typeof arg === 'string') {
192
return arg.toLowerCase().trim();
193
}
194
if (arg && typeof arg === 'object') {
195
// Extract only relevant properties
196
const { id, type, status } = arg;
197
return { id, type, status };
198
}
199
return arg;
200
});
201
};
202
203
// Update based on data freshness
204
const updateForFreshData = (key: any[]) => {
205
const [item] = key;
206
if (item && item.updatedAt) {
207
const oneHourAgo = Date.now() - 60 * 60 * 1000;
208
return item.updatedAt > oneHourAgo;
209
}
210
return true; // Update if no timestamp
211
};
212
213
const processItem = (item: {
214
id: string;
215
type: string;
216
status: string;
217
data: any;
218
updatedAt?: number;
219
metadata?: any;
220
}) => {
221
return {
222
processedId: item.id,
223
processedType: item.type.toUpperCase(),
224
processedStatus: item.status,
225
processedAt: Date.now()
226
};
227
};
228
229
const advancedMemoized = moize
230
.transformArgs(normalizeAndExtract) // Normalize cache keys
231
.updateCacheForKey(updateForFreshData) // Update for fresh data
232
.maxSize(50) // Limit cache size
233
.maxAge(3600000) // 1 hour TTL
234
.profile('item-processing') // Performance monitoring
235
(processItem);
236
237
// Chaining with other specialized methods
238
const apiProcessor = async (endpoint: string, params: Record<string, any>) => {
239
const response = await fetch(endpoint, {
240
method: 'POST',
241
body: JSON.stringify(params),
242
headers: { 'Content-Type': 'application/json' }
243
});
244
return response.json();
245
};
246
247
const normalizeApiArgs = (key: any[]) => {
248
const [endpoint, params] = key;
249
return [
250
endpoint.toLowerCase(),
251
params ? Object.keys(params).sort().reduce((sorted, k) => {
252
sorted[k] = params[k];
253
return sorted;
254
}, {} as any) : {}
255
];
256
};
257
258
const smartApiMemoized = moize.promise
259
.transformArgs(normalizeApiArgs)
260
.serialize
261
.maxAge(60000)
262
(apiProcessor);
263
```
264
265
### Performance Considerations
266
267
- **Transformation overhead**: Complex transformations add computation cost on every function call
268
- **Cache key size**: Transformed keys should be reasonably sized for memory efficiency
269
- **Conditional updates**: `updateCacheForKey` functions should be fast to avoid performance penalties