What could be causing my TypeScript object patch function to fail when passed empty patch objects?

Check out this TypeScript function I wrote, it's designed to apply a patch to an object where the patch is a partial object of the same type:

function applyUpdate<T extends object>(obj: T, update: Readonly<Partial<T>>): T {
  const updatedObj = { ...obj, ...update };
  console.log(`obj: ${JSON.stringify(obj)}`);
  console.log(`update: ${JSON.stringify(update)}`);
  console.log(`updatedObj: ${JSON.stringify(updatedObj)}`);
  return updatedObj;
}

This function seemed to work fine until I encountered a strange issue while testing it in the typescript playground.

It works for most cases, but not when the patch object is empty like this:{}.

No matter what I try, I can't figure out why the output looks like this:

obj: {"id":"test","data":"testdata"}
patch: {}
patched: {"id":"test"}

How did the data property just disappear in this scenario?

I was definitely expecting the data property to remain intact after running this code.

Answer №1

It's unclear where the patch object originates or how it is generated. However, one possible scenario is if the patch is not an empty object {}, but rather { testdata: undefined }. When using JSON.stringify, the undefined property will not be serialized and may appear to have disappeared.

let obj = {"id": "foo", "testdata": "bar" }
let patch = { "testdata": undefined }
let patched = {...obj, ...patch}

console.log("obj", obj);
console.log("obj", JSON.stringify(obj));
console.log("patch", patch);
console.log("patch", JSON.stringify(patch));
console.log("patched", patched);
console.log("patched", JSON.stringify(patched));

Depending on the desired outcome in such a situation, you could consider removing the undefined values from the patch object.

let obj = {"id": "foo", "testdata": "bar" }
let patch = { "testdata": undefined }

for (let k in patch) {
  if (patch[k] === undefined) delete patch[k];
}

let patched = {...obj, ...patch}

console.log("obj", obj);
console.log("obj", JSON.stringify(obj));
console.log("patch", patch);
console.log("patch", JSON.stringify(patch));
console.log("patched", patched);
console.log("patched", JSON.stringify(patched));

Answer №2

Embedded! Deep merging is required:

import merge from "lodash.merge";

function applyChanges<T extends object>(originalObj: T, changes: Readonly<Partial<T>>): T {
  const updatedObj = merge({}, originalObj, changes);
  console.log(`originalObj: ${JSON.stringify(originalObj)}`);
  console.log(`changes: ${JSON.stringify(changes)}`);
  console.log(`updatedObj: ${JSON.stringify(updatedObj)}`);
  return updatedObj;
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Typescript's conditional types provide a convenient way to work with nullable values

Is there a way to determine if a type is nullable and if it has a conditional type on the value? I attempted to do so with the following code: type IsNullable<T> = T extends null ? true : false; However, I encountered some issues: type test = IsN ...

Ionic causing delay in updating ngModel value in Angular 2

My ion-searchbar is set up like this: <ion-searchbar [(ngModel)]="searchQuery" (keyup.enter)="search();"></ion-searchbar> In the typescript file, the searchQuery variable is defined as follows: export class SearchPage { searchQuery: string ...

Traversing an array of objects in TypeScript and appending to a separate array if not already present

I have been given an array containing objects in the following format: export interface Part { workOrder?: string; task?: string; partNumber?: string; qty?: number; image?: string; name?: string; } My goal is to loop through each object in th ...

Updating icon of a row in a table based on its position with *ngFor

I am currently working with a table that contains a list of songs. My goal is to toggle the source of an icon in a specific row based on its index (for actions like play/pause, like/unlike). However, at the moment, the icon only changes in the first row re ...

Angular: Clicking on a component triggers the reinitialization of all instances of that particular component

Imagine a page filled with project cards, each equipped with a favorite button. Clicking the button will mark the project as a favorite and change the icon accordingly. The issue arises when clicking on the favorite button causes all project cards to rese ...

Error: Unable to locate the type definition file for the '@babel' package

I am currently working on a new project and here is the content of my package.json file. { "name": "dapp-boilerplate", "version": "1.0.0", "main": "index.js", "license": "MI ...

Can someone guide me on incorporating static methods into a Mongoose model using TypeScript for Mongoose version 5.11?

When using Mongoose 5.10, I implemented static methods in my Mongoose models with the following pattern: import { Schema, Document, Model, Connection } from "mongoose"; import { v4 } from "uuid"; export interface IFoo extends Document ...

What is the best way to store an audio Blob in the repository file system?

Currently, I have set up a system to record user audio through the device's microphone and can successfully download it on the same device. However, my goal now is to store this audio in my database by making an API call. How can I efficiently send th ...

What is the best way to effectively utilize Material UI breakpoints in combination with styled components?

Here is my code: const FooterBox = styled(Box)` background: #4e738a; left: 0; right: 0; bottom: 0; width: 100%; color: #ffffff; ${p => p?.theme?.breakpoints.up('xs')} { margin: auto; display: flex; flex-direction ...

Using Typescript and React to render `<span>Text</span>` will only display the text content and not the actual HTML element

My function is a simple one that splits a string and places it inside a styled span in the middle. Here's how it works: splitAndApplyStyledContent(content: string, textType: string, separator: string) { const splittedContent = content.split(separat ...

How can I exclude TypeScript files generated from js in WebStorm?

Using the Enable Typescript Compiler option results in a .js file being generated for every .ts and .tsx file by the TypeScript compiler. https://i.sstatic.net/Yr0lR.jpg When performing code completion, WebStorm does not recognize that the files were aut ...

How to retrieve the default type returned by a function using a custom TypeMap

I have a function that returns a promise with a type provided by generics. const api = <Model>(url: string) => Promise<Model> Currently, I always need to set the type of the returned data. const data = await api<{id: string, name: string ...

utilize console.log within the <ErrorMessage> element

Typically, this is the way the <ErrorMessage> tag from Formik is utilized: <ErrorMessage name="email" render={(msg) => ( <Text style={styles.errorText}> ...

The properties of cloned objects in ThreeJS are unable to be defined

While working in ThreeJS using TypeScript, I encountered an issue when attempting to clone a custom object that extends Object3D. The problem arises when the field, which is required for creating a new object, becomes undefined during the cloning process. ...

Add a React component to the information window of Google Maps

I have successfully integrated multiple markers on a Google Map. Now, I am looking to add specific content for each marker. While coding everything in strings works fine, I encountered an issue when trying to load React elements inside those strings. For ...

How to update an Angular 2 component using a shared service

My question is regarding updating components in Angular 4. The layout of my page is as follows: Product Component Product Filter Component Product List Component I am looking to link the Product Filter and Product List components so that when a user c ...

Updating shared variables is a challenge when passing functions as props to multiple functional components

In my work on implementing a navbar feature in the next.js framework, I have developed two functions for managing the mobile interface of the website. These functions handle the opening and closing of the navbar, utilizing a hamburger menu for opening and ...

Using an array of strings as a key in React with TypeScript to access values from state

import React, { useState } from 'react'; interface PropsInterface { keys: string; // name,email,password } const Poster: React.FC<PropsInterface> = (props: PropsInterface) => { const [state, setState] = useState({ name: ' ...

Ending the connection in SignalR upon $destroy

I am currently working on a page that is utilizing SignalR, Angular, and Typescript. Upon the destruction of the current $scope (such as during a page change), I make an attempt to disconnect the client from the SignalR server: Controller.ts $scope.$on(&q ...

Angular Karma Error - MatDialogRef Provider Not Found

While testing with Angular Karma, I encountered the following error... NullInjectorError: StaticInjectorError(DynamicTestModule)[ManageProblemsComponent -> MatDialogRef]: StaticInjectorError(Platform: core)[ManageProblemsComponent -> MatDialogRef]: ...