Home > Enterprise >  How to use Typescript to define a generic function signature
How to use Typescript to define a generic function signature

Time:11-20

I have this dataset:

const dataset = [
  { date: "2022-01-01", category: "red", value: 10 },
  { date: "2022-01-01", category: "blue", value: 20 },
  { date: "2022-01-01", category: "gold", value: 30 },
  { date: "2022-01-01", category: "green", value: 40 },
  { date: "2022-01-02", category: "red", value: 5 },
  { date: "2022-01-02", category: "blue", value: 15 },
  { date: "2022-01-02", category: "gold", value: 25 },
  { date: "2022-01-02", category: "green", value: 35 }
];

And I need to flat the dataset to get this:

const flattenDataset = [
  { "date": "2022-01-01", "red": 10, "blue": 20, "gold": 30, "green": 40 },
  { "date": "2022-01-02", "red": 5, "blue": 15, "gold": 25, "green": 35 }
]

So group dataset by dates and for each category create a key with value as value. I created this function:

export function flatDataset(
  dataset: any,
  mainProperty: string,
  categoryProperty: string,
  valueProperty: string
) {
  if (dataset.length === 0 || !mainProperty) {
    return (dataset as unknown);
  }
  const columnToBeFlatValues = uniqBy(dataset, categoryProperty).map(
    (d) => d[categoryProperty]
  );
  const datasetGroupedByMainProperty = groupBy(dataset, mainProperty);
  const datasetGroupedByMainCategoryFlat = Object.entries(
    datasetGroupedByMainProperty
  ).map(([date, datasetForDate]) => {
    const categoriesObject = columnToBeFlatValues.reduce((acc, value) => {
      const datum = datasetForDate.find(
        (d) => d[mainProperty] === date && d[categoryProperty] === value
      );
      acc[value] = datum?.[valueProperty];
      return acc;
    }, {});
    return {
      [mainProperty]: date,
      ...categoriesObject
    };
  });
  return datasetGroupedByMainCategoryFlat;
}

It works but I would like to fix TypeScript. For example the dataset type should not be any but an array of objects with keys with name mainProperty, categoryProperty, valueProperty.

For example, dataset could also be:

const dataset = [
  { apple: ..., color: ..., something: ... },
  ...
];
const flattenDataset = flatDataset(dataset, 'apple', 'color', 'something')

How can I do that?

CodePudding user response:

There's a few way to make it

let obj = { apple: 's', sonic: 'a' , etc: 'd'}

function MyFunction<T, K extends keyof T>(obj: T, ...args:K[]): void { }

MyFunction(obj, 'apple', 'sonic', 'etc')

Using Generics with Rest Arguments. I used this post as example to create the rest args. I recommend you create an interfaces for maintein all sorted

CodePudding user response:

I believe this is the right generic types you're looking for.
The main trick is using { [k in Enum]: string }, which allows dynamically restricting a key of an object to be a part of a union of strings.

Please note you're using functions without their implementations, so it's hard to actually test it.

function flatDataset<Main extends string, Category extends string, Value extends string>(
    dataset: ({ [k in Main | Category]: string } & { [k in Value]: number })[],
    mainProperty: Main,
    categoryProperty: Category,
    valueProperty: Value,
): ({ [k in (typeof dataset)[number][Main]]: string }
    & { [k in (typeof dataset)[number][Category]]: number })[]
  • Related