Skip to content

Instantly share code, notes, and snippets.

@christianscott
Last active March 27, 2019 22:43
Show Gist options
  • Save christianscott/9bb3642fd11bab6f01759e324bf9dbc0 to your computer and use it in GitHub Desktop.
Save christianscott/9bb3642fd11bab6f01759e324bf9dbc0 to your computer and use it in GitHub Desktop.
Memoize decorator
interface IMemoizeCache<K, V> {
has(key: K): boolean;
get(key: K): V | undefined;
set(key: K, value: V): this;
}
type MapKey = string | number | symbol
class ArrayKeyedMap<K extends any[], V> implements IMemoizeCache<K, V> {
private readonly map: Map<MapKey, V> = new Map()
private static defaultKey = <A extends any[]>(array: A) => array[0];
public static new = <K extends any[], V>() => new ArrayKeyedMap<K, V>()
constructor(
private readonly key: (array: K) => MapKey = ArrayKeyedMap.defaultKey,
) {}
has(array: K) {
return this.map.has(this.key(array))
}
get(array: K) {
return this.map.get(this.key(array))
}
set(array: K, value: V) {
this.map.set(this.key(array), value);
return this
}
}
function memoize<A extends any[], R>(
makeCache: () => IMemoizeCache<A, R> = ArrayKeyedMap.new,
): any {
type F = (...args: A) => R;
return (
target: unknown,
propertyName: string,
propertyDescriptor: TypedPropertyDescriptor<F>,
) => {
const cache = makeCache();
if (propertyDescriptor.value != null) {
propertyDescriptor.value = makeMemoizedFunction(cache, propertyDescriptor.value.bind(target));
} else {
throw new Error('can only decorate methods')
}
}
}
function makeMemoizedFunction<A extends any[], R>(
cache: IMemoizeCache<A, R>,
originalFunction: (...args: A) => R,
): (...args: A) => R {
return (...args: A): R => {
if (cache.has(args)) {
return cache.get(args);
}
const value = originalFunction(...args)
cache.set(args, value)
return value
}
}
class Maths {
@memoize()
public increment(n: number) {
return n + 1
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment