type ThenReturn < T > = T extends Promise < infer U > ? U : T extends ( ( ... args : any [ ] ) => Promise < infer V > ) ? V : T ; param { AsyncFunction } fn param { Function } hasher returns export function memoize < FnType extends ( ... args : any [ ] ) => Promise < any > > ( fn , hasher = ( ... args ) => args [ 0 ] , timeoutMs ? ) { const memos = new Map ( ) ; const queues = new Map ( ) ; const returnFn = async ( ... args ) : Promise < ThenReturn < FnType > > => { const key = hasher ( ... args ) ; if ( memos . has ( key ) ) { if ( ! timeoutMs || Date . now ( ) < memos . get ( key ) ! . expiration ) { return memos . get ( key ) ! . value ; } } if ( queues . has ( key ) ) { return await queues . get ( key ) ! ; } const promise = fn ( ... args ) ; queues . set ( key , promise ) ; try { const ret = await queues . get ( key ) ! ; memos . set ( key , { value : ret , expiration : Date . now ( ) + ( timeoutMs || 0 ) } ) ; return ret ; } finally { queues . delete ( key ) ; } } ; const reset = ( ... args ) : void => { const key = hasher ( ... args ) ; if ( memos . has ( key ) ) { memos . delete ( key ) ; } } ; const clear = ( ) : void => { memos . clear ( ) ; } ; ( returnFn as any ) . reset = reset ; ( returnFn as any ) . clear = clear ; return returnFn as FnType & { reset : FnType ; clear : ( ) => void ; } ; }