-
Notifications
You must be signed in to change notification settings - Fork 8
/
index.ts
41 lines (37 loc) · 1.46 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
export interface MemoizeOptions<A extends unknown[], R, H = unknown> {
/**
* Provides a single value to use as the Key for the memoization.
* Defaults to `JSON.stringify` (ish).
*/
hash?: (...args: A) => H
/**
* The Cache implementation to provide. Must be a Map or Map-alike.
* Defaults to a Map. Useful for replacing the cache with an LRU cache or similar.
*/
cache?: Map<H, R>
}
export type MemoizableFunction<A extends unknown[], R extends unknown, T extends unknown> = (this: T, ...args: A) => R
export function defaultHash<A extends unknown[], H extends unknown>(...args: A): H {
// JSON.stringify ellides `undefined` and function values by default. We do not want that.
return JSON.stringify(args, (_: unknown, v: unknown) => (typeof v === 'object' ? v : String(v))) as H
}
export default function memoize<A extends unknown[], R extends unknown, T extends unknown, H extends unknown>(
fn: MemoizableFunction<A, R, T>,
opts: MemoizeOptions<A, R, H> = {}
): MemoizableFunction<A, R, T> {
const {hash = defaultHash, cache = new Map<H, R>()} = opts
return function (this: T, ...args: A): R {
const id = hash.apply(this, args)
if (cache.has(id)) return cache.get(id)!
let result = fn.apply(this, args)
if (result instanceof Promise) {
// eslint-disable-next-line github/no-then
result = result.catch(error => {
cache.delete(id)
throw error
}) as R
}
cache.set(id, result)
return result
}
}