//tslint:disable-next-line const Symbol = require('es6-symbol'); export type Traversable = T | { [key: string]: Traversable }; export type ValueArg = string | number | Symbol; function traverseGet(ctx: Traversable, ...args: string[]): T { return args.reduce((cur: any, key) => cur ? cur[key] : undefined, ctx); } function traverseSet(ctx: Traversable, val: any, ...args: string[]) { const length = args.length; const last = args[length - 1]; const ancestors = args.slice(0, -1); const parent = ancestors.reduce( (cur: any, key) => { if (!cur[key]) { cur[key] = {}; } return cur[key]; }, ctx); parent[last] = val; } const endMarker = Symbol('memoize-end-marker'); export function memoize(memory: Traversable, func: (...args: ValueArg[]) => T): (...args: ValueArg[]) => T { const marker = Symbol(); return (...args: ValueArg[]) => { const prev: T = traverseGet.call(null, memory, marker, ...args, endMarker); if (prev !== undefined) { return prev; } const result: T = func.call(null, ...args); traverseSet.call(null, memory, result, marker, ...args, endMarker); return result; }; }