/* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable @typescript-eslint/no-unsafe-assignment */ export function debounce< T extends ( ...args: any[] ) => void >( fn: T, delay: number, options: { leading?: boolean; trailing?: boolean } = {} ) { let timer: ReturnType< typeof setTimeout > | null = null; let lastArgs: Parameters< T > | null = null; const { leading = false, trailing = true } = options; return function ( ...args: Parameters< T > ): void { const callNow = leading && ! timer; lastArgs = args; if ( timer ) { clearTimeout( timer ); } timer = setTimeout( () => { timer = null; if ( trailing && lastArgs ) { fn( ...lastArgs ); lastArgs = null; } }, delay ); if ( callNow ) { fn( ...args ); lastArgs = null; } }; } export function isObject( value: unknown ): value is Record< string, any > { return typeof value === 'object' && value !== null; } export function once< T extends ( ...args: any[] ) => any >( fn: T ): T { let called = false; let result: ReturnType< T >; return function ( ...args: Parameters< T > ) { if ( ! called ) { called = true; result = fn( ...args ); } return result; } as T; } export function pick< T extends object, K extends keyof T >( obj: T, keys: ReadonlyArray< K > ): Pick< T, K > { const result = {} as Pick< T, K >; for ( const key of keys ) { if ( key in obj ) { result[ key ] = obj[ key ]; } } return result; } export function throttle< T extends ( ...args: any[] ) => void >( fn: T, wait: number, options: { leading?: boolean; trailing?: boolean } = {} ) { let timer: ReturnType< typeof setTimeout > | null = null; let lastArgs: Parameters< T > | null = null; let lastInvokeTime = 0; const { leading = true, trailing = true } = options; const invoke = ( time: number ) => { lastInvokeTime = time; fn( ...lastArgs! ); lastArgs = null; }; return ( ...args: Parameters< T > ): void => { const now = Date.now(); if ( ! lastInvokeTime && ! leading ) { lastInvokeTime = now; } const remaining = wait - ( now - lastInvokeTime ); lastArgs = args; if ( remaining <= 0 || remaining > wait ) { if ( timer ) { clearTimeout( timer ); timer = null; } invoke( now ); } else if ( ! timer && trailing ) { timer = setTimeout( () => { timer = null; if ( lastArgs ) { invoke( Date.now() ); } }, remaining ); } }; }