Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infer the argument that the debounced function is expected to take #55

Closed
juanmendes opened this issue Oct 28, 2021 · 1 comment
Closed

Comments

@juanmendes
Copy link

juanmendes commented Oct 28, 2021

For code like the following, the argument to the actual input handler is not inferred, it's any

document.querySelector('input').addEventListener(
  'input',
   // e here is any, should be Event
  debounce((e) => console.log('Typed text', e.target.value), 500)
);

However, it is possible to infer the type based on what it's being assigned to. As in, assigned to the 2nd parameter for addEventListener as above, or if you did something like the following:

// We can know that the type of a is string here
const lateLog: (s: string) => void = debounce((a) => console.log(a));

The following is an args inferring version of a much simpler debounce than you have implemented. Please ignore the return type of any, which is just used to minimize noise and focus on inferring the arguments.

function argInferringDebounce<Args extends unknown[]>(
  func: (...args: Args) => any,
  timeout = 300
): (...args: Args) => void {
  let timer: number;
  return (...args) => {
    clearTimeout(timer);
    timer = setTimeout(() => {
      func.apply(this, args);
    }, timeout);
  };
}

The idea is that you don't need to declare the entire (...args: any[]) => any; as a generic, or a Procedure as you have called it.

By moving the generic part just to the arguments of the function (<Args extends unknown[]>), this magical inference occurs.

Here's a stackblitz showing it in action

@chodorowicz
Copy link
Owner

chodorowicz commented Nov 14, 2021

Hey @juanmendes!
Thanks for bringing up this use case. You're right that in this case (i.e inferring types from the function signature where debounce was used) was not working and was inferring any type.

The change was not so easy unfortunately 😅 Since we're inferring also ReturnType<F> and ThisParameterType<F> we can't use just Args in generic arguments. I've come up with a way that it works, maybe there's more elegant way to express it, but I haven't found it so far

export declare function debounce<Args extends any[], F extends (...args: Args) => any>(func: F, waitMilliseconds?: number, options?: Options<ReturnType<F>>): {
    (this: ThisParameterType<F>, ...args: Parameters<F> & Args): Promise<ReturnType<F>>;
    cancel: (reason?: any) => void;
};

Your example correctly infers Event type with version 4.0. https://stackblitz.com/edit/typescript-yzekbv?file=index.ts

Thanks again for the idea, I've added you to the all-contributors list.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants