Skip to content

Inferring Callback Types

Narrative

When making HTTP requests to the REST API, there were times when each component file ran at the same time. At this time, I decided that modifying the function definition side rather than modifying the user side would minimize the modification, so I solved it by creating a debounce function. In some cases, there are no function arguments, and in other cases, function arguments are required, so the callback type is inferred.

Type Inference Code

ts
export const debounce = <
  Prop,
  Callback extends PropCallback | VoidCallback,
  VoidCallback = () => void,
  PropCallback = (prop: Prop) => void,
>(
  callback: Callback extends (prop: Prop) => void
    ? (prop: Prop) => void
    : () => void,
  ms = 0,
) => {
  let timer: null | ReturnType<typeof setTimeout> = null;

  return (...props: Prop[]) => {
    timer && clearTimeout(timer);

    timer = setTimeout(() => {
      callback(props[0]);
    }, ms);
  };
};

Usage Examples

It can be used as follows:

ts
debounce(() => {
  console.log("empty");
}, 100);
debounce((num: number) => {
  console.log(num);
}, 100);
debounce(({ num, str }: { num: number; str: string }) => {
  console.log(num, str);
}, 100);

When more than one function argument is used in a callback, the following TS error occurs:

ts
// TS2345: Argument of type '(num: number, str: string) => void' is not assignable to parameter of type '(props: number) => void'
debounce((num: number, str: string) => {
  console.log(num, str);
}, 100);