A secure way to perform a deep update on any type, even if it is completely different from the original

Is there a method to eliminate the as any in the update_substate function? It seems type-safe when directly invoking the update_state function, so it should also be safe when invoked indirectly, right? These functions are meant to be lightweight helpers for managing Redux state. I have come across some related questions ([1], [2], [3]), but I haven't fully grasped the solutions yet.

export function update_state <
    RootState,
    P1 extends keyof RootState,
    S1 extends RootState[P1],
> (root_state: RootState, path1: P1, replacement_state: S1)
{
    const current = root_state[path1]
    if (current === replacement_state) return root_state

    return {
        ...root_state,
        [path1]: replacement_state
    }
}


export function update_substate <
    RootState,
    P1 extends keyof RootState,
    S1 extends RootState[P1],
    P2 extends keyof S1,
    S2 extends S1[P2],
> (root_state: RootState, path1: P1, path2: P2, replacement_substate: S2)
{
    /**
    Without the `as any` will get the error:

    Argument of type 'RootState[P1]' is not assignable to parameter of type 'S1'.
    'S1' could be instantiated with an arbitrary type which could be unrelated to 'RootState[P1]'.
        Type 'RootState[keyof RootState]' is not assignable to type 'S1'.
        'S1' could be instantiated with an arbitrary type which could be unrelated to 'RootState[keyof RootState]'.
            Type 'RootState[string] | RootState[number] | RootState[symbol]' is not assignable to type 'S1'.
            'S1' could be instantiated with an arbitrary type which could be unrelated to 'RootState[string] | RootState[number] | RootState[symbol]'.
                Type 'RootState[string]' is not assignable to type 'S1'.
                'S1' could be instantiated with an arbitrary type which could be unrelated to 'RootState[string]'.ts(2345)
    */
    const replacement_state = update_state<S1, P2, S2>(root_state[path1] as any, path2, replacement_substate)
    return update_state(root_state, path1, replacement_state)
}

Answer №1

If you want the snippet to compile (without verifying its content), follow these steps:

  1. Remove the type parameter S1 since it is not utilized in the update_substate function signature.
  2. Allow parameter types to be inferred automatically instead of explicitly declaring them with update_state <S1, P2, S2>.
  3. Adjust the generic constraint of P2 to be P2 extends keyof RootState[P1].
export function update_substate <
    RootState,
    P1 extends keyof RootState,
    P2 extends keyof RootState[P1],
    S2 extends RootState[P1][P2],
> (root_state: RootState, path1: P1, path2: P2, replacement_substate: S2)
{
    const replacement_state = update_state(root_state[path1], path2, replacement_substate)
    return update_state(root_state, path1, replacement_state)
}

Live code

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 steps should I take to import a module with type definitions? (receiving error TS2656: ...not a module)

I am currently working on enhancing the type definitions for a simple npm module called emitter20. The source code of this module spans 20 lines and looks like this: module.exports = function() { var subscribers = [] return { on: function (eventNa ...

Tips on changing the date format in Typescript to the desired format

My date string reads as 2016-09-19T18:10:31+0100. Here's what I'm doing: let dateString:string = 2016-09-19T18:10:31+0100; let newDateString:Date = new Date(dateString); The output I'm currently getting is Tue Sep 19 2016 18:10:31 GMT+0530 ...

Utilize multiple activated modules in Angular 2

Recently, I've been exploring the new features of Angular 2 final release, particularly the updated router functionality. An interesting example showcasing the router in action can be found at this link: http://plnkr.co/edit/mXSjnUtN7CM6ZqtOicE2?p=pr ...

Exporting symbols within the same namespace from multiple files in a TypeScript project

I have a typescript module and I am looking to define symbols within the 'aaa' namespace from multiple files. Here is an example of what my code looks like: a.ts: export namespace aaa { export const a = "a"; } b.ts: export namespac ...

Expanding Classes through Index signatories

My attempt at creating an abstract class is not going as smoothly as I hoped. I suspect my limited knowledge of TypeScript is the primary issue, even though this seems like a common scenario. The abstract class I'm working on is called Program. It co ...

What is the method for assigning 'selective-input' to a form field in Angular?

I am using Angular and have a form input field that is meant to be filled with numbers only. Is there a way to prevent any characters other than numbers from being entered into the form? I want the form to behave as if only integer keys on the keyboard ar ...

Encountering a snag when trying to load JavaScript within an HTML document

I encountered an error while trying to load an HTML file in the JavaScript console of the Brave browser. The error message reads: require.js:5 Uncaught Error: Module name "constants.js" has not been loaded yet for context: _. Use require([]) https://requir ...

Guide on Implementing Link href in Next.js v12

When I set the href as a string, the link functions properly. However, when I use an object for the href, the link fails to work. Despite seeing the correct querystring when hovering over the link, it always takes me back to the first page. // ProdCard t ...

How to vertically align Material UI ListItemSecondaryAction in a ListItem

I encountered an issue with @material-ui/core while trying to create a ListItem with Action. I am looking for a way to ensure that the ListItemSecondaryAction stays on top like ListItemAvatar when the secondary text becomes longer. Is there any solution to ...

PhantomJS version 2.1.1 encountered an error on a Windows 7 system, displaying "ReferenceError: Map variable not found."

I've been utilizing the "MVC ASP.NET Core with Angular" template. I'm attempting to incorporate phantomJS and execute the tests, but encountering the following errors: ERROR in [at-loader] ..\\node_modules\zone.js\dist&bs ...

Issues arise as a result of conflicts between the dependencies of @ionic/angular, Angular 13, typescript,

Current Environment Details: ionic info Ionic: Ionic CLI : 6.18.1 (/usr/local/lib/node_modules/@ionic/cli) Ionic Framework : @ionic/angular 5.8.5 @angular-devkit/build-angular : 13.0.2 @angular-devkit/schemat ...

Retrieve the final variable in an Observable sequence

In my code, I have a variable called 'messages' which stores messages from a conversation: messages: Observable<Message[]>; To populate the 'messages' variable, I do the following: const newMessage = new Message(objMessage); ne ...

React-file-viewer shrinks any document to a compact size

I have been searching extensively for information on how to adjust file sizing in react-file-viewer without any success. My objective is to utilize the react-file-viewer to allow users to click on a filename hyperlink and open the file (be it an image, do ...

Webpack 5: Updating the file path for TypeScript declaration files

My project structure includes a crucial src/ts folder: - dist/ - js/ - css/ - index.html - about.html - src/ - assets/ - fonts/ - images/ - sass/ - ts/ - services/ - service1.ts - ...

When using a Redux action type with an optional payload property, TypeScript may raise complaints within the reducer

In my react-ts project, I define the following redux action type: type DataItem = { id: string country: string population: number } type DataAction = { type: string, payload?: DataItem } I included an optional payload property because there are tim ...

The ng test option is failing to execute effectively

Attempting to conduct unit tests utilizing Karma and Jasmine through the ng test is proving to be a bit challenging. Upon issuing the following command: ng test --watch=false --code-coverage --main ./src/main/resources/public/scripts/xyz/workspace/commons ...

What is the best way to set a Firestore data field as a variable?

I am working with a firebase collection named 'messages', where I add documents as follows: this.afs.collection('messages').add({ 'qn': message, 'student': this.student, 'upvotes': this.upvote }); The upv ...

Is it necessary to also include template-only attributes in my controller's definition?

I'm working on a directive template that features a basic toggle variable. <div ng-mouseenter="$ctrl.myToggle = true" ng-mouseleave="$ctrl.myToggle = false"> ... </div> <div ng-if="$ctrl.myToggle"> ... toggled content </div> ...

What is the best way to implement a custom layout with nuxt-property-decorator?

Summary of Different Header Components in Nuxt In order to set a different header component for a specific page in Nuxt, you can create separate layout files. layout ├ default.vue // <- common header └ custom.vue // <- special header for s ...

What is the proper way to compare enum values using the greater than operator?

Here is an example enum: enum Status { inactive = -1, active = 0, pending = 1, processing = 2, completed = 3, } I am trying to compare values using the greater than operator in a condition. However, the current comparison always results in false ...