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

Clickable link that directs to a particular tab on a webpage (cbpFWTabs)

I have been utilizing tabs from the codrops Tab Styles Inspiration and I am looking for a way to open specific tabs using URLs. For instance, if I wanted to open tab3, I could link it as www.google.com/index#tab3. Below is the code I am currently using: ...

ajax ignores output from php

I've been working on passing PHP echo values through AJAX, but I've encountered a problem where the if and else conditions are being skipped in the success function of AJAX. Even when the if condition is met, the else statements are still being e ...

Transferring an organized array to processing

My goal is to transfer an array of integers from a php file called load.php to a JS script, which will then forward it to a Processing file written in JavaScript. In load.php, I define the array and send it using JSON (the array contains a minimum of 40 i ...

Tips for managing asynchronous REST errors in unit tests with Jest

Below is the code snippet for my Node.js test file. This unit test case is failing, here are the details of the code and error message: jest.unmock('./utils.js'); describe('test', () => { it('test', async (done) => ...

learning how to transfer a value between two different components in React

I have 2 components. First: component.ts @Component({ selector: "ns-app", templateUrl: "app.component.html", }) export class AppComponent implements OnInit { myid: any; myappurl: any; constructor(private router: Router, private auth: ...

Unable to establish communication with server. Facing issues in connecting AngularJS with NodeJS

I am currently working on establishing a communication process between my server and client to receive either a "success" or "failure" response. The server is being developed using node.js with the express framework, while the client is built using angular ...

ScriptManager is not accessible in the current ASP.Net Core Razor Page context

I'm facing an issue where I have a view (such as Index.cshtml) and a page model (like Index.cshtml.cs). In the view, there's a JavaScript function that I want to call from the OnPost() method in the page model. I tried using ScriptManager for thi ...

Guide: "Changing an HTML element's class by utilizing its id attribute"

This is the HTML code snippet I am working with: <li class="treeview" id="account_management"> I need to select the element with the id of "account_management" and update its class from "treeview" to "treeview active" in order to ...

The radio button fails to trigger the setState function in React

In my React program, I have implemented a method to update the state and modify a specific value for a Radio button: The state : class App extends Component { constructor(props) { super(props); this.state = { checked: [], expanded: [], ...

What are the best ways to store internal files in node.js for faster access?

I have been utilizing routing functions like the one mentioned below to replicate the overall design of my website (A.jade): exports.overview = function(req, res, next) { res.render('A', { main: jade.renderFile('./views/B.jade' ...

How can I find the week of the month using Moment.js? (similar to Google Calendar)

Currently leveraging the fantastic features of Moment.js, I am facing a dilemma. How can I determine which week of the month a particular date falls into? The documentation for Moment js only seems to provide information on "week of year." For instance, if ...

Executing the onSuccess callback in Ajax without any ability to manipulate the ajax requests

My dilemma lies in needing to execute a JavaScript function upon the successful completion of an AJAX call. Unfortunately, I am unable to directly manage the AJAX calls as they are handled by the DNN5 framework. Is there a way for me to trigger my functio ...

Is there a way to prevent the jsonPayload in stackdriver from automatically converting to a struct format?

When working with a Google Cloud Function, I am logging a simple JSON structure like this: console.info(JSON.stringify({message: 'xxx', data: {status: 200}, status: 200})); However, the log appears in an unreadable format in Stackdriver. How ca ...

What is the process for bundling a separate JavaScript file with Webpack5?

I am new to the world of webpack. I have 2 javascript files and I want to designate one as the main entry file. However, for the other file, I only want to include it in specific files. For example, looking at the file structure below, main.js is my entr ...

A groundbreaking algorithm engineered to launch a sequence of distinct hyperlinks upon each button press

I have a unique script that allows me to open links randomly, but now I want to create a new script that opens links sequentially and goes back to the initial one. For example, clicking a button will open link1, then clicking it again will open link2, an ...

Issue accessing page from side menu in Ionic 2 application

I am experiencing an issue where the page does not open when I click on it in the side menu. Here is my app.component.ts file: this.pages = [ { title: 'NFC Page', component: NfcPage, note: 'NFC Page' }, ...

Improve the way you manage the active selection of a button

ts isDayClicked: { [key: number]: boolean } = {}; constructor() { } setSelectedDay(day: string, index: number): void { switch (index) { case 0: this.isDayClicked[0] = true; this.isDayClicked[1] = false; this.isDay ...

The Bootstrap carousel spiraled into disarray

I am facing an issue with the basic bootstrap carousel. My goal is to make the slides move every four seconds. The current setup of the carousel code is as follows: $(document).ready(function() { fixCarousel(); }); function fixCarousel() { $('.c ...

Exploring Angular 1.5: A guide to binding a transcluded template to a component's scope

Currently, I am utilizing a form component that includes common validation and saving functions. Inputs are injected into the form as transcluded templates in the following manner: <form-editor entity="vm.entity"> <input ng-model="vm.dirt ...

What is the process for verifying a particular user in AngularJS?

I'm new to AngularJS and I'm a bit confused about the concepts of GET, PUT requests. I am currently working on an app where I display a list of users on one page, and on another page, I have a form with three buttons. My main focus is on the "Con ...