The issue lies in the constraint (...args: any[]) => void
on F
, which can accommodate various types unexpectedly, causing the function you're returning to not be assignable to these types. For instance:
debounce(() => "oopsie", 1000)().toUpperCase(); // compiles fine but throws TypeError at runtime
In this scenario, the function type F
returns a value of type string
, which is valid for a function that should return void
due to TypeScript behavior as described in the FAQ. However, since debounce()
does not return a function with string as its return type, the return type of debounce()
does not match the input F
.
In addition:
function foo() { };
foo.prop = 123;
debounce(foo, 1000).prop.toFixed(); // compiles without errors but raises TypeError at runtime
In this case, the function has a property declared on it, leading the type F
to be a function type () => void
with an additional property prop
. Once again, since debounce()
does not return a function with such extra property, the returned function's type does not match the input F
.
To resolve this, make debounce()
generic enough to represent your intended functionality. The returned function should accept the same arguments as the passed-in function, necessitating the argument list to be generic. The output function will strictly return void
without any additional properties. Thus, only the argument list requires a type parameter (e.g., A
), resulting in both the input and output functions having the type (...args: A) => void
:
export function debounce<A extends any[]>(
fn: (...args: A) => void,
timeout: number
): (...args: A) => void {
let timer: NodeJS.Timeout | undefined;
return ((...args: A) => {
if (timer) clearTimeout(timer);
timer = setTimeout(() => fn(...args), timeout);
});
}
This code compiles error-free. Best of luck!
Link to code