[SOLVED] How to dispatch calculation to relevant function according to input's type?

Issue

When writing a function in TypeScript, is there a way to let the function infer what to do according to the input’s type?

For example, let’s say that I have a function that calculates the maximum value.

  • In scenario A, the input is a numeric array (i.e., type: number[]), and I want it to return the max value. So I could do:

    const calcMaxArr = (arr: number[]): number => {
      return Math.max(...arr) // https://stackoverflow.com/a/39106546/6105259
    }
    
  • In scenario B I also want to calculate the max value, but this time my input data is an object, and I want to return the key that corresponds to the largest value. In this case I could do:

    const calcMaxObj = (obj: Record<string, number>): string => {
       return Object.keys(obj).reduce((a, b) => obj[a] > obj[b] ? a : b); //https://stackoverflow.com/a/27376421/6105259
    }
    

While these two functions (calcMaxArr() & calcMaxObj()) work perfectly well, I wonder whether I could unify them somehow under one function calcMax(). That is, I wonder whether calcMax() can infer, from the input’s type, whether to defer/dispatch the calculation to calcMaxArr() or to calcMaxObj().

If type: number[] -> calcMaxArr()
If type: Record<string, number> -> calcMaxObj()

Is there a built-in feature in typescript for such kind of a task?


EDIT


I ran across this tweet that demonstrates a similar procedure but in python. For those who know python, this makes a useful analogy in my opinion.


EDIT 2


I’ve now learned that what I am asking about actually has a name: My calcMax() is a generic function, and one built-in implementation of such generic functions is seen in Common Lisp, for example.

Solution

Does this work for you…

const calcMax = (inputs: number[] | Record<string, number>): number | string => {
  if (Array.isArray(inputs)) {
    return calcMaxArr(inputs);
  }
  return calcMaxObj(inputs);
}

const calcMaxArr = (arr: number[]): number => {
  return Math.max(...arr) // https://stackoverflow.com/a/39106546/6105259
}

const calcMaxObj = (obj: Record<string, number>): string => {
  return Object.keys(obj).reduce((a, b) => obj[a] > obj[b] ? a : b); //https://stackoverflow.com/a/27376421/6105259
}

Illustration

"use strict";
const calcMax = (inputs) => {
  if (Array.isArray(inputs)) {
    return calcMaxArr(inputs);
  }
  return calcMaxObj(inputs);
};

const calcMaxArr = (arr) => {
  return Math.max(...arr); // https://stackoverflow.com/a/39106546/6105259
};

const calcMaxObj = (obj) => {
  return Object.keys(obj).reduce((a, b) => obj[a] > obj[b] ? a : b); //https://stackoverflow.com/a/27376421/6105259
};

console.log(calcMax([10, 8, 12, 3]));
console.log(calcMax({
  "10": 10,
  "8": 8,
  "12": 12,
  "3": 3
}));

WYSIWYG => WHAT YOU SHOW IS WHAT YOU GET

Answered By – Nalin Ranjan

Answer Checked By – Katrina (BugsFixing Volunteer)

Leave a Reply

Your email address will not be published.