Suppose you have a function, written in TypeScript, that is fine and complete. Now you want to write a function that works like that one but with just some slightly more parameters and other differences. It's not too different from a decorator function.

Extending the list of parameters

Let's imagine that you already have this function:


type Operation = "sum" | "sub" | "mul" | "div"

function calculator(a: number, b: number, op: Operation) {
  if (op === "sum") return a + b
  if (op === "sub") return a - b
  if (op === "mul") return a * b
  if (op === "div") return a / b
  throw new Error(op)
}

And this could, for the sake of argument, be in a different file and/or possibly outside your control. What you now want to do is write a new function that takes those same arguments, plus one more. And importantly, you don't want to have to spell out all the arguments one more time.

First, the WRONG way to solve this:


import { calculator } from './calculator' // The code in the snippet above

type Operation = "sum" | "sub" | "mul" | "div"

function doubleCalculator(a: number, b: number, op: Operation, c: number) {
  return calculator(calculator(a, b, op), c, op)
}

console.log(calculator(1, 2, "sum")) // 3
console.log(doubleCalculator(1, 2, "sum", 10)) // 13

Technically, it works, but you had to repeat the calculator function's signature.

The RIGHT way to solve this:


import { calculator } from './calculator' // The code in the snippet above

function doubleCalculator(c: number, ...args: Parameters<typeof calculator>) {
  return calculator(calculator(...args), c, args[2])
}

console.log(calculator(1, 2, "sum")) // 3
console.log(doubleCalculator(10, 1, 2, "sum")) // 13

The key in this is that the list of types could be cumbersomely large. In this example calculator there are just 3 parameters. But it could be 9 and most importantly, you might not care.

Not touching the parameters

Suppose you want to write a function that just calls another function but with some added logging. In this case, it's even easier :


import { calculator as _calculator } from "./base";

function calculator(...args: Parameters<typeof _calculator>) {
  console.warn(`About to ${args[2]} ${args[0]} and ${args[1]}`);
  return _calculator(...args);
}

console.log(calculator(1, 2, "sum")); 
// About to sum 1 and 2
// 3 

One parameter which is an object

Instead of a long list of parameters, let's imagine it's just 1 single parameter and it's an object.
Imagine we start with this:


type Operation = "sum" | "sub" | "mul" | "div";

type Props = {
  a: number;
  b: number;
  op: Operation;
};

export function calculator({ a, b, op }: Props) {
  if (op === "sum") return a + b;
  if (op === "sub") return a - b;
  if (op === "mul") return a * b;
  if (op === "div") return a / b;
  throw new Error(op);
}

Now, you want to "extend" that similarly to what we did before. Here's one way to solve that:


import { calculator } from "./calculator";

type Props = Parameters<typeof calculator>[0] & { c: number };

function doubleCalculator(props: Props) {
  const { c, ...rest } = props;
  return calculator({ a: calculator(rest), b: c, op: rest.op });
}

console.log(calculator({ a: 1, b: 2, op: "sum" })); // 3
console.log(doubleCalculator({ a: 1, b: 2, op: "sum", c: 10 })); // 13

Comments

Your email will never ever be published.

Related posts