Thomas Krog Horne
Thomas Krog Horne

Reputation: 53

Typescript: Map properties from one object to another

I have the following two types and object.

type TypeA = {
  a: number;
  b: number;
  c: number;
};
type TypeB = {
  d: number;
  e: number;
  c: number;
};
oldObject: { [key: string]: TypeA };

I want to map the properties from an object of type TypeA to an object of type TypeB. So far I've come up with the following.

const newObject: { [key: string]: TypeB } = {};

Object.entries(oldObject).forEach(([key, value]) => {
  newObject[key] = {
    d: value.a,
    e: value.b,
    c: value.c,
  } as TypeB;
});

Is there any way of doing this in typescript with something like a map or reduce function? I would much prefer to not have to assign {} to newObject at first.

Upvotes: 5

Views: 11089

Answers (2)

Aluan Haddad
Aluan Haddad

Reputation: 31833

You can use Object.fromEntries.

type TypeA = {
    a: number;
    b: number;
    c: number;
};
type TypeB = {
    d: number;
    e: number;
    c: number;
};

declare const oldObject: { [key: string]: TypeA };

const newObject: { [key: string]: TypeB } = Object.fromEntries(
    Object.entries(oldObject).map(([key, value]) => [
        key,
        {
            d: value.a,
            e: value.b,
            c: value.c
        }
    ])
);

Playground Link

To elaborate a little, Object.fromEntries is, as its name suggests, the inverse of Object.entries, taking an array of [key, value] elements and creating an object where the keys are mapped to their corresponding values.

Thus, we start with Object.entries as before but, instead of using forEach to populate our new object, we map over the array, transforming each [key, value] to [key, newValue] and pass the resulting array to Object.fromEntries.

Note that, as jcalz remarks, Object.fromEntries was introduced in ES2019. If your target runtime does not support ES2019, you will need to add a polyfill such as npm:object.fromentries and correspondingly specify "lib": ["ES2019"] or higher in your tsconfig.json

I've removed the type assertion as TypeB since it is both unnecessary and would more importantly would suppress errors like forgetting or misspelling a property declared by TypeB. Type inference is one of TypeScript's most important aspects.

Your intuition around using map and reduce is a very natural one. Not only have we used map here, but reduce was frequently used to achieve the same result as Object.fromEntries before the latter was introduced.

Upvotes: 1

MoxxiManagarm
MoxxiManagarm

Reputation: 9124

You could also use lodash's mapValue

const oldObject = {
  x: { a: 1, b: 2, c: 3 },
  y: { a: 11, b: 22, c: 33 },
}

function map(value) {
  return {
    d: value.a,
    e: value.b,
    f: value.c
  };
}

const newObject = mapValues(oldObject, map);

console.log(newObject);

Upvotes: 0

Related Questions