memoize-one.ts 1.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. import areInputsEqual from './are-inputs-equal';
  2. export type EqualityFn<TFunc extends (...args: any[]) => any> = (
  3. newArgs: Parameters<TFunc>,
  4. lastArgs: Parameters<TFunc>,
  5. ) => boolean;
  6. export type MemoizedFn<TFunc extends (this: any, ...args: any[]) => any> = {
  7. clear: () => void;
  8. (this: ThisParameterType<TFunc>, ...args: Parameters<TFunc>): ReturnType<TFunc>;
  9. };
  10. // internal type
  11. type Cache<TFunc extends (this: any, ...args: any[]) => any> = {
  12. lastThis: ThisParameterType<TFunc>;
  13. lastArgs: Parameters<TFunc>;
  14. lastResult: ReturnType<TFunc>;
  15. };
  16. function memoizeOne<TFunc extends (this: any, ...newArgs: any[]) => any>(
  17. resultFn: TFunc,
  18. isEqual: EqualityFn<TFunc> = areInputsEqual,
  19. ): MemoizedFn<TFunc> {
  20. let cache: Cache<TFunc> | null = null;
  21. // breaking cache when context (this) or arguments change
  22. function memoized(
  23. this: ThisParameterType<TFunc>,
  24. ...newArgs: Parameters<TFunc>
  25. ): ReturnType<TFunc> {
  26. if (cache && cache.lastThis === this && isEqual(newArgs, cache.lastArgs)) {
  27. return cache.lastResult;
  28. }
  29. // Throwing during an assignment aborts the assignment: https://codepen.io/alexreardon/pen/RYKoaz
  30. // Doing the lastResult assignment first so that if it throws
  31. // the cache will not be overwritten
  32. const lastResult = resultFn.apply(this, newArgs);
  33. cache = {
  34. lastResult,
  35. lastArgs: newArgs,
  36. lastThis: this,
  37. };
  38. return lastResult;
  39. }
  40. // Adding the ability to clear the cache of a memoized function
  41. memoized.clear = function clear() {
  42. cache = null;
  43. };
  44. return memoized;
  45. }
  46. export default memoizeOne;