type mpObj = { [k in keyof T | string | number | symbol]: any }; export function apply( target: mpObj, patchItem: mpObj ): Partial & Partial; export function apply(target: mpObj, patchItem: mpObj): R; export function apply(target: mpObj, patchItem: mpObj): {}; export function apply(target: mpObj, patchItem: null): null; export function apply(target: mpObj, patchItem: string): string; export function apply(target: mpObj, patchItem: number): number; export function apply(target: mpObj, patchItem: undefined): undefined; export function apply(target: mpObj, patchItem: R[]): R[]; export function apply(target: any, patchItem: any): any { /** * If the patch is anything other than an object, * the result will always be to replace * the entire target with the entire patch. */ if (typeof patchItem !== "object" || Array.isArray(patchItem) || !patchItem) { return JSON.parse(JSON.stringify(patchItem)); //return new instance of variable } if ( typeof patchItem === "object" && patchItem.toJSON !== undefined && typeof patchItem.toJSON === "function" ) { return patchItem.toJSON(); } /** Also, it is not possible to * patch part of a target that is not an object, * such as to replace just some of the values in an array. */ let targetResult = target; if (typeof target !== "object") { //Target is empty/not an object, so basically becomes patch, minus any null valued sections (becomes {} + patch) targetResult = { ...patchItem }; } Object.keys(patchItem).forEach((k) => { if (!targetResult.hasOwnProperty(k)) targetResult[k] = patchItem[k]; //This ensure the key exists and TS can't throw a wobbly over an undefined key if (patchItem[k] === null) { delete targetResult[k]; } else { targetResult[k] = apply(targetResult[k], patchItem[k]); } }); return targetResult; }