the HTTP sepecification for the PATCH method describes it as a more selective way of updating a resource. this can be easily implemented in typescript or javascript as demonstrated below:
export default function patchObjects(original: any, patch: any) {
const originalCopy = { ...original };
Object.keys(patch).forEach((key) => {
const matchingOriginalProp = originalCopy[key];
if (typeof matchingOriginalProp === 'object') {
originalCopy[key] = patchObjects(matchingOriginalProp, patch[key]);
} else {
originalCopy[key] = patch[key];
}
});
return originalCopy;
}
Tests:
import patchObjects from './object.helpers';
type TestDataType = {
name: string;
details: {
age: number;
attributes: {
height: number;
weight: number;
eyeColor: string;
};
};
};
describe('PatchObjects', () => {
it('replaces fields that have been passed in to be patched', () => {
const original: TestDataType = {
name: 'xxx',
details: {
age: 1,
attributes: {
height: 111,
weight: 222,
eyeColor: 'blue',
},
},
};
const patchData = {
details: {
attributes: {
weight: 666,
},
},
};
const actual = patchObjects(original, patchData);
expect(actual).toEqual({
name: 'xxx',
details: {
age: 1,
attributes: {
height: 111,
weight: 666,
eyeColor: 'blue',
},
},
});
});
it('does not replace fields if they are not in patch', () => {
const original: TestDataType = {
name: 'xxx',
details: {
age: 1,
attributes: {
height: 111,
weight: 222,
eyeColor: 'blue',
},
},
};
const patchData = {};
const actual = patchObjects(original, patchData);
expect(actual).toEqual(original);
});
it('adds fields in if they are in patch', () => {
const original = {
details: {
age: 1,
attributes: {
height: 111,
weight: 222,
},
},
};
const patchData = {
details: {
name: 'john',
attributes: {
color: 'grey',
},
},
};
const actual = patchObjects(original, patchData);
expect(actual).toEqual({
details: {
name: 'john',
age: 1,
attributes: {
color: 'grey',
height: 111,
weight: 222,
},
},
});
});
it('only replaces fields with undefined if they are explicitly being patched', () => {
const original: TestDataType = {
name: 'xxx',
details: {
age: 1,
attributes: {
height: 111,
weight: 222,
eyeColor: 'blue',
},
},
};
const patchData = {
details: {
age: 1,
attributes: {
eyeColor: undefined,
},
},
};
const actual = patchObjects(original, patchData);
expect(actual).toEqual({
name: 'xxx',
details: {
age: 1,
attributes: {
height: 111,
weight: 222,
eyeColor: undefined,
},
},
});
});
});