Home > other >  TypeScript How To Deep Update An Object With Partial
TypeScript How To Deep Update An Object With Partial

Time:11-03

I have an interface:

export interface SalesOrder {
        id: number,
        DeliveryInfo: {
           Address: Object,
           DeliveryTime: string
        }
}

I would like a function where I can pass in a full Sales Order, as well as a partial one. I would like a new object, which is the same as the old object except all of its properties are replaced with properties that exist in the partial object.

So for example, I'd like to update in a Sales Order with a new DeliveryTime. What I would do is pass in a full SalesOrder object, and a partial one with just DeliveryInfo.DeliveryTime:

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

The issue is that when I do this, rather than updating the DeliveryTime on the sales order, it completely replaces the DeliveryInfo object with the partial one. I understand that Recursive Partials can be achieved with something like

export type RecursivePartial<T> = {
    [P in keyof T]?: RecursivePartial<T[P]>;
};

But I am totally unclear on how to implement that in my solution.

This app has a lot of complicated types, with properties that are nested deep. Id like a system set up where a full object and partial object can be passed in, and an updated object will be returned

Here's a stackblitz demonstrating this: https://stackblitz.com/edit/typescript-o9rtej?file=index.ts

I need to be able to update any property in the SalesOrder type, no matter how deep it is and if it is an array or not, etc.

CodePudding user response:

I'm sure there are a million examples of this function on SO, but anyways the strategy is to

  1. look at each key/value pair and if it exists on the partial object use that value
  2. if the value is an object then recurse
  3. if it doesn't exist on the partial object then use the original value
const rmap = (obj, pobj) => {
    return Object.entries(obj).reduce((acc, [key, value]) => {
        if (!(key in pobj)) return { ...acc, [key]: value };
        if (typeof pobj[key] === "object" && pobj[key] !== null && !Array.isArray(pobj[key])) {
            return { ...acc, [key]: rmap(obj[key], pobj[key]) };
        } else {
            return { ...acc, [key]: pobj[key] };
        }
    }, {});
};
  • Related