Type a debounce wrapper that preserves the wrapped function's parameters
debounce takes a function and a delay, and returns a function that calls the original only after ms have passed with no further call.
Requirements:
every call site — no any[], no Function;
- the returned function must accept exactly the parameters of the wrapped function, checked at
- calling it again before the delay elapses restarts the timer;
- the returned function returns
void.
export function debounce(fn, ms) {
// your code here
}
Write the implementation.
Make the wrapper generic over the function type, <T extends (...args: any[]) => void>, and type the returned function (...args: Parameters<T>) => void. The caller gets the wrapped parameters checked at every call, not an any[].
- ✗Typing the wrapped function as
Function, which drops every parameter check - ✗Returning
(...args: any[]) => void, so the wrapper's caller loses the original arity - ✗Forgetting that a debounced call cannot return the wrapped function's result
- →How would you also preserve the receiver with
ThisParameterType<T>? - →What changes if the debounced call must return a
Promiseof the result?
The wrapper is generic over the function type, not over its arguments. Parameters<T> then extracts the original parameter tuple, and it is checked at every call site.
export function debounce<T extends (...args: any[]) => void>(
fn: T,
ms: number,
): (...args: Parameters<T>) => void {
let timer: ReturnType<typeof setTimeout> | undefined;
return (...args: Parameters<T>): void => {
if (timer !== undefined) clearTimeout(timer);
timer = setTimeout(() => fn(...args), ms);
};
}
const save = debounce((id: number, body: string) => post(id, body), 300);
save(1, 'hi'); // ok
save('1', 'hi'); // Error: 'string' is not assignable to 'number'
T extends (...args: any[]) => void is only a constraint: the any[] is visible inside the wrapper, while the precise Parameters<T> is what leaves it. Type fn as Function instead and both save(1, 'hi') and save('nonsense') compile.
The result is void: at the moment the wrapper returns, the call has not happened, so there is nothing to return.