My task was simple: write a function wrapper to measure the time it takes to execute a function. It’s easy to write a function wrapper in JavaScript but in TypeScript this is hard because we need to preserve the types of the wrapped function. It took me a while to figure out how do this so I’m leaving this here so future me doesn’t have to waste as much time as past me did.

function instrumentFn<F extends (...args: any[]) => ReturnType<F>>(id: string, fn: F) {
  return (...args: Parameters<F>) => {
    const t1 = performance.now();

    const result = fn(...args);

    const t2 = performance.now();

    console.log(`${id} took ${t2 - t1}ms`);

    return result;
  };
}

The tricky parts are these:

  • F extends (...args: any[]) => Type to type fn as a generic function.
  • ReturnType<Type> to get the return type of fn.
  • Parameters<Type> to get the types of the parameters of fn.

And here’s an example of using instrumentFn:

function fibonacci(n: number): number {
   if (n < 1) {
     return 0;
   } else if (n <= 2) {
     return 1;
   } else {
     return fibonacci(n - 1) + fibonacci(n - 2);
   }
}

const instrumentedFibonacci = instrumentFn('fibonacci', fibonacci);

instrumentedFibonacci(30);
// fibonacci took 11ms

Pretty cool.