import mimicFn from 'mimic-fn' ; import type { AsyncReturnType } from 'type-fest' ; type AnyAsyncFunction = ( ... arguments_ : readonly any [ ] ) => Promise < unknown | void > ; const cacheStore = new WeakMap < AnyAsyncFunction , CacheStorage < any , any > > ( ) ; const promiseCacheStore = new WeakMap < AnyAsyncFunction , Map < unknown , unknown > > ( ) ; interface CacheStorage < KeyType , ValueType > { has : ( key : KeyType ) => Promise < boolean > | boolean ; get : ( key : KeyType ) => Promise < ValueType | undefined > | ValueType | undefined ; set : ( key : KeyType , value : ValueType ) => void ; delete : ( key : KeyType ) => void ; clear ? : ( ) => void ; } interface Options < FunctionToMemoize extends AnyAsyncFunction , CacheKeyType > { default readonly cachePromiseRejection ? : boolean ; default example readonly cacheKey ? : ( arguments_ : Parameters < FunctionToMemoize > ) => CacheKeyType ; default example readonly cache ? : CacheStorage < CacheKeyType , AsyncReturnType < FunctionToMemoize > > ; } param fn example export default function pMemoize < FunctionToMemoize extends AnyAsyncFunction , CacheKeyType > ( fn , { cachePromiseRejection = false , cacheKey , cache = new Map < CacheKeyType , AsyncReturnType < FunctionToMemoize > > ( ) , } = { } ) { const promiseCache = new Map < CacheKeyType , Promise < AsyncReturnType < FunctionToMemoize > > > ( ) ; const memoized = async function ( this , ... arguments_ ) : Promise < AsyncReturnType < FunctionToMemoize > > { const key = cacheKey ? cacheKey ( arguments_ ) : arguments_ [ 0 ] as CacheKeyType ; if ( await cache . has ( key ) ) { if ( promiseCache . has ( key ) ) { return promiseCache . get ( key ) ! ; } return ( await cache . get ( key ) ) ! ; } const promise = fn . apply ( this , arguments_ ) as Promise < AsyncReturnType < FunctionToMemoize > > ; promiseCache . set ( key , promise ) ; try { const result = await promise ; cache . set ( key , result ) ; return result ; } catch ( error ) { if ( ! cachePromiseRejection ) { promiseCache . delete ( key ) ; } throw error as Error ; } } as FunctionToMemoize ; mimicFn ( memoized , fn , { ignoreNonConfigurable : true , } ) ; cacheStore . set ( memoized , cache ) ; promiseCacheStore . set ( memoized , promiseCache ) ; return memoized ; } returns example pMemoizeDecorator pMemoizeDecorator export function pMemoizeDecorator < FunctionToMemoize extends AnyAsyncFunction , CacheKeyType > ( options = { } ) { const instanceMap = new WeakMap ( ) ; return ( target , propertyKey , descriptor ) : void => { const input = target [ propertyKey ] ; if ( typeof input !== 'function' ) { throw new TypeError ( 'The decorated value must be a function' ) ; } delete descriptor . value ; delete descriptor . writable ; descriptor . get = function ( ) { if ( ! instanceMap . has ( this ) ) { const value = pMemoize ( input , options ) as FunctionToMemoize ; instanceMap . set ( this , value ) ; return value ; } return instanceMap . get ( this ) as FunctionToMemoize ; } ; } ; } param fn export function pMemoizeClear ( fn ) { const cache = cacheStore . get ( fn ) ; if ( ! cache ) { throw new TypeError ( 'Can\'t clear a function that was not memoized!' ) ; } if ( typeof cache . clear !== 'function' ) { throw new TypeError ( 'The cache Map can\'t be cleared!' ) ; } cache . clear ( ) ; promiseCacheStore . get ( fn ) ! . clear ( ) ; }