// source: https://github.com/andreypopp/memoize-decorator const SENTINEL = {}; export function memoize(target: any, name: string, descriptor: TypedPropertyDescriptor) { if (typeof descriptor.value === 'function') { return (_memoizeMethod(target, name, descriptor) as any) as TypedPropertyDescriptor; } else if (typeof descriptor.get === 'function') { return _memoizeGetter(target, name, descriptor) as TypedPropertyDescriptor; } else { throw new Error( '@memoize decorator can be applied to methods or getters, got ' + String(descriptor.value) + ' instead', ); } } function _memoizeGetter(target: any, name: string, descriptor: PropertyDescriptor) { const memoizedName = `_memoized_${name}`; const get = descriptor.get!; target[memoizedName] = SENTINEL; return { ...descriptor, get() { if (this[memoizedName] === SENTINEL) { this[memoizedName] = get.call(this); } return this[memoizedName]; }, }; } function _memoizeMethod(target: any, name: string, descriptor: TypedPropertyDescriptor) { if (!descriptor.value || (descriptor.value as any).length > 0) { throw new Error('@memoize decorator can only be applied to methods of zero arguments'); } const memoizedName = `_memoized_${name}`; const value = descriptor.value; target[memoizedName] = SENTINEL; return { ...descriptor, value() { if (this[memoizedName] === SENTINEL) { this[memoizedName] = (value as any).call(this); } return this[memoizedName] as any; }, }; }