Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
April 7, 2022 04:46 pm GMT

Polish types for the new debounce function

There is always a room for improvement, so let's take a look what can be improved in the debounce function we've created in the previos article:

// debounce.function.tsexport function debounce<A = unknown, R = void>(  fn: (args: A) => R,  ms: number): [(args: A) => Promise<R>, () => void] {  let timer: NodeJS.Timeout;  const debouncedFunc = (args: A): Promise<R> =>      new Promise((resolve) => {          if (timer) {              clearTimeout(timer);          }          timer = setTimeout(() => {              resolve(fn(args));          }, ms);      });  const teardown = () => clearTimeout(timer);  return [debouncedFunc, teardown];}

It does the job, but looks clunky:

  • timer type tied to Nodejs;
  • does not allow multiple primitive arguments, i.e. two numbers;
  • return type is hard to read.

We'll start with the easiest one, the timer type. Instead of typing it using NodeJS.Timeout we could type it in a more sly way with ReturnType:

let timer: ReturnType<typeof setTimeout>;

So timer is whatever setTimeout returns, no arguing that.

Now perhaps to the most interesting part: allow passing to the debounce function any amount of arguments of any type instead of one stictly typed object.

To get there first we need to understand an interface that is applicaple to any function in typescript, if we gave it a name, and let's say, called it FunctionWithArguments, it would look the following way:

// ./models/function-with-arguments.model.tsexport interface FunctionWithArguments {  (...args: any): any;}

This single interface will allow us to eliminate the necessity to type separately the argument type and the return type in debounce<A = unknown, R = void>, we could go straight to a type that expects a function instead of argument + return type, which would look like this: debounce<F extends FunctionWithArguments>(fn: F, ms: number).

So we get a function F which is an extension of FunctionWithArguments, how would a debounce function interface look like then? It would take the aforementioned function and utilise types Parameters and ReturnType generics to unpack whatever arguments and return type the F function carries:

// ./models/debounced-function.model.tsimport { FunctionWithArguments } from './function-with-arguments.model';export interface DebouncedFunction<F extends FunctionWithArguments> {  (...args: Parameters<F>): Promise<ReturnType<F>>;}

As you can see, DebouncedFunction accepts any function, and produces a function which is its async version, without the need to explicitly pass arguments and return types.

Having dealt with the first two points, it is time now to make return type of debounce a bit more readable.

[(args: A) => Promise<R>, () => void] basically equals to Array<DebouncedFunction<F> | (() => void)>, so we can strictly type it by creating a separate interface:

// ./models/debounce-return.model.tsimport { DebouncedFunction } from './debounced-function.model';import { FunctionWithArguments } from './function-with-arguments.model';export interface DebounceReturn<F extends FunctionWithArguments> extends Array<DebouncedFunction<F> | (() => void)> {  0: (...args: Parameters<F>) => Promise<ReturnType<F>>;  1: () => void;}

There we go, a strictly typed tuple.

Putting it all together we get a better typed debounce function, which no longer requires passing argument and return type explicitly, but infers them from the passed function insead:

// debounce.function.tsimport { DebouncedFunction, DebounceReturn, FunctionWithArguments } from './models';export function debounce<F extends FunctionWithArguments>(fn: F, ms: number): DebounceReturn<F> {  let timer: ReturnType<typeof setTimeout>;  const debouncedFunc: DebouncedFunction<F> = (...args) =>    new Promise((resolve) => {      if (timer) {        clearTimeout(timer);      }      timer = setTimeout(() => {        resolve(fn(...args as unknown[]));      }, ms);    });  const teardown = () => {    clearTimeout(timer);  };  return [debouncedFunc, teardown];}

Try it live here.

The repo is here.

Packed as npm package here.


Original Link: https://dev.to/bwca/polish-types-for-the-new-debounce-function-5e0n

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To