Tips for creating a TypeScript-compatible redux state tree with static typing and immutability:

One remarkable feature of TypeScript + Redux is the ability to define a statically typed immutable state tree in the following manner:


        interface StateTree {
          readonly subState1: SubState1;
          readonly subState2: SubState2;
        }

        interface SubState1 {
          readonly a: number;
          readonly b: string;
        }

        interface SubState2 {
          readonly c: {
            readonly d: symbol;
            readonly e: string;
          };
        }
    

This approach eliminates the need for libraries like ImmutableJS and provides strong guarantees when accessing the read-only state tree, as shown here:


        const state: StateTree = getState();
        state.subState2.c.e
    

However, the question arises: Is there a method to maintain immutability and type-safety while creating modified copies of these read-only state trees?

I initially considered using Object.assign(), which ensures immutability but lacks compile-time type-safety due to TypeScript's behavior with type intersection (refer to this discussion).

Answer №1

When I initially set up my React & Redux project, I overlooked the importance of immutability. Now, I'm faced with the task of retroactively incorporating it into my codebase. As I explore different approaches, I prioritize maintaining TypeScript's strong typing capabilities. While Immutable.js didn't seem to offer a seamless solution for type checking in my project, I came across a promising alternative: ImmutableAssign.

https://github.com/engineforce/ImmutableAssign

ImmutableAssign aims to integrate effectively with TypeScript, which aligns perfectly with my requirements. So far, it appears to meet my expectations by updating references throughout nested objects when a property is modified deep within the chain. However, as I continue implementing ImmutableAssign in my project, I have yet to assess its performance characteristics. Nonetheless, it's worth exploring this tool as you navigate similar challenges.

Answer №2

Using the "readonly" keyword is an important feature when striving for immutability, but it only takes effect during compilation.

When working with immutable.js, you take a step further as it ensures immutability at runtime.

While there isn't a one-size-fits-all solution that combines deep cloning and type safety perfectly, Object.assign does provide shallow copying which often suffices when managing states in Redux.

I don't believe that using Object.assign compromises compile-time safety. The structural typing system in Typescript verifies compatibility by ensuring that all required members are assigned properly. For example:

interface State {
    a: number;
}

const myVar = Object.assign({}, {b: 456}) as State;

In this case, the code will fail to compile because a is missing in myVar.

Answer №3

Upon careful consideration of the discussion provided at the mentioned link, I have come to realize that my initial belief regarding the limitations of Object.assign() in TypeScript due to type intersection was actually unfounded. The true underlying issue lies with object assignment itself.

The actual problem stems from object assignment rather than type intersection in TypeScript.

I had mistakenly attributed the issue to type intersection when it is clearly rooted in object assignment:

interface Combined {
  a: number;
  b: string;
}

interface A {
  a: number;
}

interface B {
  b: string;
}

const temp1: Combined = {
  a: 1,
  b: '2'
};

const temp2: A = temp1;

This scenario does not involve any type intersection.

An interface should be viewed as just that – an interface, not a concrete type.

In reference to the code snippet above, my confusion around the validity of the statement const temp: A = temp1; arose from a misconception rooted in type intersection.

However, upon closer inspection, I now understand the rationale behind its validation.

The A interface is not a tangible type like a primitive or a class; rather, it serves as an interface.

Final Thoughts

In summary...

TypeScript's effective utilization of interface and readonly, combined with JavaScript's Object.assign(), provide optimal compile-time type safety and immutability.

While these tools offer substantial support, the responsibility for further verification primarily rests on practices such as unit testing and code review.

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

Looking to cycle through arrays containing objects using Vue

Within my array list, each group contains different objects. My goal is to iterate through each group and verify if any object in a list meets a specific condition. Although I attempted to achieve this, my current code falls short of iterating through eve ...

What is the best way to save the properties of elements in an array of objects within another array?

I have obtained attributes from objects within an array that I need to store in another array. Here is the data I am working with: My goal is to extract the `displays` name attribute and save it in the `opt[]` array, which would result in something like t ...

How can I send a value to an Angular element web component by clicking a button with JavaScript?

I want to update the value of an input in an Angular component by clicking on a button that is outside of the Angular Element. How can I achieve this in order to display the updated value in the UI? Sample HTML Code: <second-hello test="First Value"&g ...

Using Typescript to override an abstract method that has a void return type

abstract class Base{ abstract sayHello(): void; } class Child extends Base{ sayHello() { return 123; } } The Abstract method in this code snippet has a return type of void, but the implementation in the Child class returns a number. S ...

Error: Unable to retrieve data through Ajax request

$(document).ready(function(){ /* Fetching Data from TTC for Union Station */ $.getJSON("http://myttc.ca/Union_station.json?callback=?", function(data){ if (routes.length > 0) { // Display the stop details with departur ...

Encountering an Object Type Unknown error while working with Typescript and React

I am currently working on building a chatbox in React using TypeScript and Firebase. Below is the code for my Room and Message components: function ChatRoom() { const messagesRef = firestore.collection('messages'); const query = messagesRef.o ...

Mongoose discovers an unexpected empty array

My task is to locate a SoldProduct with an intimeOrderNumber value of '3'. var query = { intimeOrderNumber: '3' }; However, my search result returns an empty array. SoldProduct.find(query, function(err, soldProducts) { if (err) { ...

Having trouble with ES6 in Canvas - why won't my code display correctly?

I'm currently working on developing a painting app using ES6. However, I'm facing issues with the positioning and line drawing on the canvas. The lines are not being drawn in the correct position; for example, the top-left corner is formed when ...

What are the best practices for utilizing an array of routes?

I'm new to working with react but I noticed something strange. My routes are currently set up like this: <Main> <Route exact path="/home" component={Home} /> <Route exact path="/home1" com ...

"An ng-repeat directive with a filter applied results in an empty

After successfully implementing the ng-repeat loop below: <div ng-repeat="einschItem in einschaetzungen.alldata | filter: { savedatum: lolatage[tagarrayindex].tagestring } | orderBy : '-savetimestamp'"> I wanted to check if the filtered r ...

Need jQuery solution for changing CSS in numerous locations upon hover

Currently, I am working on a WordPress website and I am trying to figure out how to change the CSS color of a side navigation element when a remote image is hovered over. In a typical scenario, I would accomplish this using CSS by assigning a hover class ...

Losing scope of "this" when accessing an Angular2 app through the window

My Angular2 app has exposed certain methods to code running outside of ng2. However, the issue arises when calling these methods outside of ng2 as the context of this is different compared to when called inside. Take a look at this link to see what exactl ...

Storing the blob received from an AJAX call into Azure Blob Storage may result in a corrupted image

When I send a PDF to my vendor's API, they return a .png file as a blob (note: see update 2 regarding the data type being returned). I want to store this in Azure Blob Storage. However, when using the code below, the uploaded file becomes corrupted. ...

A guide on determining the number of rows in an ag-grid with TypeScript and Protractor

I am currently working on extracting the number of rows in an ag-grid. The table is structured as follows: <div class="ag-body-container" role="presentation" style="height: 500px; top: 0px; width: 1091px;"> <div role="row" row-index="0" row-id="0 ...

I am wondering if it is feasible for a POST route to invoke another POST route and retrieve the response ('res') from the second POST in Express using Node.js

Currently, I have a POST route that triggers a function: router.route('/generateSeed').post(function(req,res){ generate_seed(res) }); UPDATE: Here is the genrate_seed() function function generate_seed(res) { var new_seed = lightwallet. ...

"Ensuring Security with Stripe Connect: Addressing Content Security Policy Challenges

Despite using meta tags to address it, the error persists and the Iframe remains non-functional. <meta http-equiv="Content-Security-Policy" content=" default-src *; style-src 'self' 'unsafe-inline'; ...

Is it possible to use Docxtemplater.js in a browser without familiarity with Node, Browserify, or npm?

I'm trying to get Docxtemplater.js to function properly on a browser. Unfortunately, I lack knowledge in areas such as Node.js, Browserify.js, "npm", "bower", and other technical aspects commonly found in modern JavaScript libraries. While I do inten ...

FullCalendar dayClick event fails to trigger any action

I'm having trouble implementing the dayClick function in my fullCalendar. Despite setting up the calendar correctly, nothing happens when I click on a day. Here is the code I am using : var calendar; $(document).ready(function(){ app.init(); ...

Update information without the need for an xhr request in the back-end using jQuery

To keep notification updated with any changes in the database without causing a slowdown to the website, I implemented the following solution: HTML Code: <div id="myid">{{ $db_data }}</div> Back-end Request (utilizing Laravel): $db_data = A ...

Is there a way to display the button only when the user is able to enter an email in the input field?

I have a form for users to update their profile and a button to delete their account. I want the delete account button to only be visible if the user enters their email. $(document).ready(function() { var user_email = $('input[name="emp_email"]&a ...