import { isObject } from "./isObject" ; export interface DebouncedFunc < T extends ( ... args : any [ ] ) => any > { /**
     * Call the original function, but applying the debounce rules.
     *
     * If the debounced function can be run immediately, this calls it and returns its return
     * value.
     *
     * Otherwise, it returns the return value of the last invokation, or undefined if the debounced
     * function was not invoked yet.
     */ ( ... args ) : ReturnType < T > | undefined ; /**
     * Throw away any pending invokation of the debounced function.
     */ cancel ( ) : void ; /**
     * If there is a pending invokation of the debounced function, invoke it immediately and return
     * its return value.
     *
     * Otherwise, return the value from the last invokation, or undefined if the debounced function
     * was never invoked.
     */ flush ( ) : ReturnType < T > | undefined ; } interface DebounceOptions { maxWait ? : number ; leading ? : boolean ; trailing ? : boolean ; } type TimeoutId = ReturnType < typeof setTimeout > ; param { Function } func param { number } wait param options param { boolean } options . leading param { number } options . maxWait param { boolean } options . trailing returns { Function } example export function debounce < T extends ( ... args : any ) => any > ( func , wait ? , options ? ) { type Result = ReturnType < T > | undefined ; let lastArgs ; let lastThis ; let result ; let lastCallTime ; let timerId ; let lastInvokeTime = 0 ; const useRAF = wait === undefined && typeof window !== "undefined" && typeof window . requestAnimationFrame === "function" ; if ( typeof func !== "function" ) { throw new TypeError ( "Expected a function" ) ; } const waitValue = Number ( wait ) || 0 ; let leading = false ; let trailing = true ; let maxWait = null ; if ( isObject ( options ) ) { leading = ! ! options . leading ; maxWait = "maxWait" in options ? Math . max ( Number ( options . maxWait ) || 0 , waitValue ) : maxWait ; trailing = "trailing" in options ? ! ! options . trailing : trailing ; } function invokeFunc ( time ) { const args = lastArgs ; const thisArg = lastThis ; lastArgs = lastThis = undefined ; lastInvokeTime = time ; result = func . apply ( thisArg , args ) ; return result ; } function startTimer ( pendingFunc , wait ) { if ( useRAF && typeof timerId === "number" ) { window . cancelAnimationFrame ( timerId ) ; return window . requestAnimationFrame ( pendingFunc ) as any ; } return setTimeout ( pendingFunc , wait ) ; } function cancelTimer ( id ) { if ( useRAF && typeof id === "number" ) { window . cancelAnimationFrame ( id ) ; } clearTimeout ( id ) ; } function leadingEdge ( time ) { lastInvokeTime = time ; timerId = startTimer ( timerExpired , waitValue ) ; return leading ? invokeFunc ( time ) : result ; } function remainingWait ( time ) { if ( lastCallTime === undefined ) return 0 ; const timeSinceLastCall = time - lastCallTime ; const timeSinceLastInvoke = time - lastInvokeTime ; const timeWaiting = waitValue - timeSinceLastCall ; return maxWait !== null ? Math . min ( timeWaiting , maxWait - timeSinceLastInvoke ) : timeWaiting ; } function shouldInvoke ( time ) { if ( lastCallTime === undefined ) return true ; const timeSinceLastCall = time - lastCallTime ; const timeSinceLastInvoke = time - lastInvokeTime ; return ( timeSinceLastCall >= waitValue || timeSinceLastCall < 0 || ( maxWait !== null && timeSinceLastInvoke >= maxWait ) ) ; } function timerExpired ( ) { const time = Date . now ( ) ; if ( shouldInvoke ( time ) ) { return trailingEdge ( time ) ; } timerId = startTimer ( timerExpired , remainingWait ( time ) ) ; return ; } function trailingEdge ( time ) { timerId = undefined ; if ( trailing && lastArgs ) { return invokeFunc ( time ) ; } lastArgs = lastThis = undefined ; return result ; } function cancel ( ) { if ( timerId !== undefined ) { cancelTimer ( timerId ) ; } lastInvokeTime = 0 ; lastArgs = lastCallTime = lastThis = timerId = undefined ; } function flush ( ) { return timerId === undefined ? result : trailingEdge ( Date . now ( ) ) ; } function pending ( ) { return timerId !== undefined ; } function debounced ( this , ... args ) { const time = Date . now ( ) ; const isInvoking = shouldInvoke ( time ) ; lastArgs = args ; lastThis = this ; lastCallTime = time ; if ( isInvoking ) { if ( timerId === undefined ) { return leadingEdge ( lastCallTime ) ; } if ( maxWait !== null ) { timerId = startTimer ( timerExpired , waitValue ) ; return invokeFunc ( lastCallTime ) ; } } if ( timerId === undefined ) { timerId = startTimer ( timerExpired , waitValue ) ; } return result ; } debounced . cancel = cancel ; debounced . flush = flush ; debounced . pending = pending ; return debounced ; }