Jeremy Gillick
Jeremy Gillick

Reputation: 2700

Return an object with properties from function parameter list

I'm trying to create a function that constructs and returns an object with properties that match the array of strings passed in the params.

type Foods = 'apple' | 'banana' | 'pear';

function foodObjects<K extends Foods>(foods: Foods[]): Record<K, number> {
    const ret = {};
    foods.forEach((food, i) => {
        ret[food] = i;
    })
    return ret;
}

foodObjects(['apple', banana']); // should return { apple: 0, banana: 1 }

In this simple example, you pass it an array of fruits and it returns an object with the fruit names as keys. However, I get a warning when defining the ret variable (const ret = {};):

Type '{}' is not assignable to type 'Record<K, number>'

I can resolve that error with the as keyword, but this doesn't feel correct:

const ret = {} as Record<K, number>;

Upvotes: 0

Views: 508

Answers (1)

Ali Habibzadeh
Ali Habibzadeh

Reputation: 11558

If you want an array to turn into an object, your best bet is to reduce it:

type Foods = "apple" | "banana" | "pear";

function foodObjects<K extends Foods>(foods: Foods[]): Record<K, number> {
  return foods.reduce((acc, food, i) => ({ ...acc, [food]: i }), <Record<K, number>>{});
}

console.log(foodObjects(["apple", "banana"])); // should return { apple: 0, banana: 1 }

Since it was pointed out that casting is not super ideal, here is a version that does not need casting:

type Foods = "apple" | "banana" | "pear";

type FoodObjects = {
  [K in Foods]: number;
};

function foodObjects(foods: Foods[]): FoodObjects {
  const entries = foods.map((food, i) => [food, i]);
  return Object.fromEntries(entries);
}

Upvotes: 2

Related Questions