Sep 30, 2020

Please your library user with typescript generic

In typescript, generic is a tool that api/library author to please its consumer. What does this means.

For example, as library developer, I want to create a higher order function to modify a an existing function, so that I can log the entering and exiting of the function 's execution. The following function does the job

function log (fn) {
  const name = fn.name;
  return function (...args) {
    console.log(`entering ${name}`);
    const rtn = fn(...args);
    console.log(`leaving ${name}`);
  };
}

function add(a:number, b: number) {
  return a + b;
}

add(1, 2);
add('1', '2'); //warning
//
const logAdd = log(add); //type information is lost
logAdd(1, 2);
logAdd('1', '2'); //no warning

When consuming the log function, the modified function run as expected, but it lost the signature of the original function, and it cannot type checking like the original function.

It is pretty easy to solve the problem by adding generic to the log function.

function log<T extends (...args) => any>(fn: T) {
  const name = fn.name;
  return function (...args) {
    console.log(`entering ${name}`);
    const rtn = fn(...args);
    console.log(`leaving ${name}`);
  } as T;
}

function add(a:number, b: number) {
  return a + b;
}

add(1, 2);
add('1', '2'); //warning
//
const logAdd = log(add); //type information is retained.
logAdd(1, 2);
logAdd('1', '2'); //warning

Now the modified function has all the type information of the original function. The way it works is that typescript infer the type of input argument, and pass along the type along.

No comments:

Post a Comment