In the scenario where I have a nested readonly array within an object, what is the best way to duplicate that object and transform the array to allow for mutations (such as inserting into Akita)?

Suppose I have the following TypeScript interface:

interface Member {
   readonly id: number;
   readonly name: string;
   readonly email: string;
   groups: <ReadonlyArray>Group
}

interface Group {
   readonly id: number;
   readonly name: string;
   readonly active: boolean;
}

Here is an example of how the data might look:

member {
   id: 1,
   name: 'John Doe',
   email: '<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="f49e819a9fb49e819a9fda979b99">[email protected]</a>'
   groups: [
      { id: 1, name: 'Group 1', active: true}
      { id: 2, name: 'Group 2', active: false}
      { id: 3, name: 'Group 3', active: false}
   ]
}

If I want to store this data in akita/state and allow for mutability (such as updating the status of a group), how do I create a mutable copy of the object before inserting it into akita/state?

After considering the comments below, I realized I need to modify my code for inserting into the akita store:

  loadMemberById(memberId: number) {
    const request$ = this.membersDataService.getById(memberId).pipe(
      tap(response => this.membersStore.upsert(response.id, response)),
      tap(response => this.membersStore.upsert(response.id, { groups: { 
         ...response.groups } })),
      tap(response => this.membersStore.setHasCache(true))
    );

    return cacheable(this.membersStore, request$);
  }

My Visual Studio is flagging state.member.groups in the arrayUpdate function.

  update(groupId: number, value: boolean): Observable<void> {
    const observer = this.groupsDataService.update(groupId, value);

    observer.subscribe(() => {
      this.sessionStore.update(state => {
        return {
          member: {
            ...state.member,
            groups: arrayUpdate(state.member.groups, data => data.groupId === 
                groupId, {
                    active: value
            })
          }
        };
      });
    });

Thank you, jonpfl

Answer №1

According to immerjs, this code snippet removes readonly attributes:

It's important to be cautious when using this code, as it allows you to modify values that may not be intended to be changed.

For best results, consider using it in conjunction with the spread operator or object.assign method.

type AtomicObject =
    | Function
    | Promise<any>
    | Date
    | RegExp
    | Boolean
    | Number
    | String

type Draft<T> = T extends AtomicObject
    ? T
    : T extends ReadonlyMap<infer K, infer V> // Map extends ReadonlyMap
    ? Map<Draft<K>, Draft<V>>
    : T extends ReadonlySet<infer V> // Set extends ReadonlySet
    ? Set<Draft<V>>
    : T extends WeakReferences
    ? T
    : T extends object
    ? {-readonly [K in keyof T]: Draft<T[K]>}
    : T

Answer №2

If you find yourself in a situation where deconstructing the object will not work because it copies the readonly state, here's what you should do:

someReadonlyObject; <-- the original immutable object
copiedObject; <-- new copied mutable object

copiedObject = JSON.parse(JSON.stringify(someReadonlyObject));

This process will result in a brand new object, making copiedObject mutable.

Just make sure to update the store correctly by using its .update() function instead of assigning individual values to ensure that the state tracks all changes.

Answer №3

implement the spread operator for copying

declaring oldState variable....
assigning a copy of somegroup to groupToAdd variable;
creating a new state object, combining oldState and adding groupToAdd to groups array

}

Answer №4

Give this a shot:

updatedGroup = { ...originalGroup }

The spread or rest operator shown here utilizes three dots to expand an object or array and extract its inner variables, similar to how parameters are handled in a function. This feature can also be employed to duplicate an instance without changing its values. In the example provided, it detaches from the original group and creates a copy of its contents.

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

What is the best way to display a Nested JSON structure without an object key?

Need help with extracting data from two different JSON structures. The first one is straightforward, but the second is nested in multiple arrays. How can I access the content? See below for the code snippets: // First JSON { "allSuSa": [ { ...

Displaying buttons based on the existence of a token in Angular - A guide

Can you assist me with a coding issue I'm facing? I have implemented three methods: login, logout, and isAuthenticated. My goal is to securely store the token in localStorage upon login, and display only the Logout button when authenticated. However, ...

Utilize TypeScript to retrieve the enumeration values as a parameter through a method that employs a generic enum type

Is there a way to retrieve all values of an Enum (specified as a parameter or generic) and return them in a list? Additionally, if the user has a specific role, I only need to retrieve certain Enum values provided as a parameter. I had the idea of groupin ...

Overriding TypeScript types generated from the GraphQL schema

Currently, I am utilizing the graphql-code-generator to automatically generate TypeScript types from my GraphQL schema. Within GraphQL, it is possible to define custom scalar types that result in specific type mappings, as seen below in the following code ...

Is it possible to customize the default typography settings for Textfields and other components using a theme in Material UI v5?

Is there a method to customize the default typography for TextField and all inputs using themes? I am aware of this approach: components: { MuiInput: { styleOverrides: { root: { fontSize: '16px', ...

Is it possible to use a Jasmine spy on a fresh instance?

In need of assistance with testing a TypeScript method (eventually testing the actual JavaScript) that I'm having trouble with. The method is quite straightforward: private static myMethod(foo: IFoo): void { let anInterestingThing = new Interesti ...

Empowering user accessibility through intricate custom elements

In the world of web accessibility guidelines, labels are typically associated with form controls like <input> or <textarea>. But what happens when dealing with complex Angular / React / ... components that function as form controls? Picture a ...

Create a personalized button | CKEditor Angular 2

I am currently working on customizing the CKEditor by adding a new button using the ng2-ckeditor plugin. The CKEditor is functioning properly, but I have a specific requirement to implement a button that will insert a Rails template tag when clicked. For ...

Angular compodoc tool is not considering *.d.ts files

Is there a way to make compodoc include .d.ts files in the documentation generation process for my Angular project? Even though I've added all .d.ts files to tsconfig.compodoc.json as shown below: { "include": [ "src/**/*.d. ...

Sort the list by the last name using Angular 2

Is there a way to arrange the contact list by LAST NAME, with names categorized under each alphabet? While I was able to achieve this in jQuery and Angular-1, I need guidance on how to implement this logic in Angular2/Ionic V2. ...

Encountered a TypeScript error: Attempted to access property 'REPOSITORY' of an undefined variable

As I delve into TypeScript, a realm unfamiliar yet not entirely foreign due to my background in OO Design, confusion descends upon me like a veil. Within the confines of file application.ts, a code structure unfolds: class APPLICATION { constructor( ...

Making tinymce editor content readonly using Angular 4

In my Angular 4 form, I am utilizing two editors. To make the editors readonly, I have implemented the following code: tinymce.activeEditor.getBody().setAttribute('contenteditable', false); tinymce.activeEditor.getBody().style.backgroundColor = ...

Tips for neatly wrapping a class constructor

Currently, I am experimenting with code to create a more streamlined Angular Dialog initializer. This initializer should be passed a constructor function along with its arguments in a type-safe manner. The current implementation works, but it is challengi ...

Navigating the world of Typescript: mastering union types and handling diverse attributes

I am currently working on building a function that can accept two different types of input. type InputA = { name: string content: string color: string } type InputB = { name: string content: number } type Input = InputA | InputB As I try to impleme ...

In Angular 4, you can easily preselect multiple options in a mat-select dropdown by passing an

Seeking assistance with setting the options of a mat-select in Angular 4. The issue at hand is as follows: 1. Initially, there are two variables: options and checkedOptions options: string[]; checkedOptions: string[] //Retrieved from the database; 2. T ...

The Kubernetes cluster unexpectedly closes down following a period of processing

My GCP cluster is hosting a NodeJS server. The server functions flawlessly when run locally, but mysteriously stops without any error messages when I attempt to send a post request to a specific route. This post request is supposed to trigger the sending o ...

Can you explain the significance of this particular method signature in the TypeScript code snippet shown above?

Referencing the ngrx example, we encounter the code snippet for the method store.select, which has a complex signature with two arrows. What is the significance of this method signature? The interface definition in the type file presents the following sig ...

What happens when the loading state does not update while using an async function in an onClick event?

I'm currently working on implementing the MUI Loading Button and encountering an issue with changing the loading state of the button upon click. Despite setting the state of downloadLoading to true in the onClick event, it always returns false. The p ...

Working with Typescript: Defining the return type of a function that extracts a subset of an object

Currently, I am attempting to create a function that will return a subset of an object's properties. However, I’m facing some issues with my code and I can't pinpoint the problem. const initialState = { count: 0, mounted: false, } type St ...

There is no initial value set for the property and it is not definitively assigned in the constructor

I encountered an issue while working on the following code snippet: export class UserComponent implements OnInit { user: User; constructor() { } ngOnInit() { this.user = { firstName : "test", lastName ...