Typescript - Update object properties dynamically with Object.entries

Viewed 5998

I want to implement a function that loops over the properties of an object and applies updates to another object of the same type.

interface Car {
  tires: number;
  name: string;
}

function updateCar(car: Car, updates: Partial<Car>) {
  Object.entries(updates).forEach(([key, value]) => {
    car[key] = value;
  })
}

The issue here is that there is an error

No index signature with a parameter of type 'string' was found on type 'Car'

when casting the key to keyof Car it works, but as soon as using "noImplicitAny": true there is again an error

Type 'string | number' is not assignable to type 'never'

Is there a way to solve this issue in a type-safe matter. Casting the car to any would work but I'd like to avoid it.

Many thanks Bene

3 Answers

This question inspired me to revisit a similar problem I ran into.

While not a direct analog of your problem (i.e. this is a pure function that returns a new object rather than modifying arguments supplied by the caller), here's an update function that retains type-safety by using object-spread instead of the Object.entries() function to do its work:

function updateFromPartial<T>(obj: T, updates: Partial<T>):T { 
    return {...obj, ...updates}; 
}

This is about as typesafe as I could get it. The only thing left not properly typed is car[key]. This is clearly not a perfect solution.

interface Car {
  tires: number;
  name: string;
}

function updateCar(car: Car, updates: Partial<Car>) {
    for (const update in updates) {
        const key = update as keyof Car;
        (car[key] as any) = updates[key];
    }
}

That function already exists. Why not just use?

Object.assign(car, updates);

The Object.assign() method copies all enumerable own properties from one or more source objects to a target object. It returns the modified target object. MDN

Related