Modifying data types within complex nested object structures

I am looking to traverse the data structure recursively and create a custom type with specific fields changed to a different type based on a condition.

Using the example structure below, I aim to generate a type (Result) where all instances of A are replaced with B types.

class A{}
class B{}

const data = {
    propA:new A,
    propB:true,
    nested:{
        propC:new A,
        propD:false
    }
}

// Example:
type Replace<typeof data, A, B>

// the expected result
type Result = {
    propA:B,
    propB:boolean,
    nested:{
        propC:B
        propD:boolean
    }

}

Answer №1

If you want to achieve this with a mapped type, remember that the matching is done based on the object structure and not the class name. This means that an object from a class C{} will also be converted when targeting class A.

The definition of the Replace type can be written as follows:

type Replace<T, From, To> = T extends (...args: any[]) => any ? T : {
  [K in keyof T]: 
    [T[K], From] extends [From, T[K]] ? To : Replace<T[K], From, To>
}

The initial condition is set to keep any methods or function properties intact as mapped types tend to change these to {}. The mapping type then goes through each key, checking if both the value extends the From type and the From type extends the value type for equality. If they are equal, the value is replaced with To, if not, the Replace function is called recursively.

Here's an example of how this conversion works:

class A{}
class B{b = 42}
class C{}

const data = {
    propA: new A(),
    propBool: true,
    propC: new C(),
    nested:{
        propA: new A(),
        propBool: false
    },
    f: () => 42
}

type Result = Replace<typeof data, A, B>
// type Result = {
//     propA: B;
//     propBool: boolean;
//     propC: B;
//     nested: {
//         propA: B;
//         propBool: boolean;
//     };
//     f: () => number;
// }

Check out the TypeScript playground

Answer №2

One way to accomplish this is by using conditional and recursive types:

class A {
    ClassPropA!: string
}
class B {
    ClassPropB!: string
}

const data = {
    propA: new A(),
    propB: true,
    nested:{
        propC: new A(),
        propD:false
    }
}

type Replace<T, A, B> = T extends object
    ? { [key in keyof T]: T[key] extends A ? B : Replace<T[key], A, B> }
    : T;

// type Result = {
//     propA: B,
//     propB: boolean,
//     nested:{
//         propC: B
//         propD: boolean
//     }
//
// }
type Result = Replace<typeof data, A, B>;

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

How can I detect if a control value has been changed in a FormGroup within Angular 2? Are there any specific properties to look

I am working with a FormGroup that contains 15 editable items, including textboxes and dropdowns. I am looking to identify whether the user has made any edits to these items. Is there a specific property or method I can use to check if the value of any i ...

When working with Visual Studio and a shared TypeScript library, you may encounter the error message TS6059 stating that the file is not under the 'rootDir'. The 'rootDir' is expected to contain all source files

In our current setup with Visual Studio 2017, we are working on two separate web projects that need to share some React components built with TypeScript. In addition, there are common JavaScript and CSS files that need to be shared. To achieve this, we hav ...

Tips for incorporating SectionList sections in React Native using an array

I am working with an array of objects named movies (const movies = movie[]). Each movie object includes properties like name, description, date and duration. movie: { name: string; description: string; date: Date; duration: number } My ...

Tips for showcasing an array containing two different types of objects in Angular

After sending a request to a remote server, I am returned with a JSON array. However, the array can contain two different types of JSON objects: [ { "country": "USA", "caseCount": 561, "caseDates": [], ...

Utilize rest parameters for destructuring操作

I am attempting to destructure a React context using rest parameters within a custom hook. Let's say I have an array of enums and I only want to return the ones passed into the hook. Here is my interface for the context type enum ConfigItem { Some ...

The type 'Dispatch<SetStateAction<boolean>>' cannot be assigned to type 'boolean'

Currently, I am attempting to transfer a boolean value received from an onChange function to a state variable. let [toggleCheck, setToggleCheck] =useState(false);` <input type="checkbox" id={"layout_toggle"} defaultChecked={toggleCh ...

Why is it important to use linting for JavaScript and TypeScript applications?

Despite searching extensively on Stack Overflow, I have yet to find a comprehensive answer regarding the benefits of linting applications in Typescript and Javascript. Any insights or resources would be greatly appreciated. Linting has become second natur ...

transition from mapStateToProps to using hooks

Just dipping my toes into the world of React (hooks) and learning by writing code. I'm grappling with converting MapStateToProps to hooks, specifically stuck on one part just before 'currentItem'. Here's the original code snippet: co ...

Using Two Unique Typeface Options in Material UI for ReactJS

Currently, in my React App, I am utilizing the Material UI library with Typescript instead of regular Javascript. I've encountered a small hurdle that I can't seem to overcome. The two typefaces I want to incorporate into my app are: Circular-S ...

Tips for refreshing the modified toggle in angular2

I currently have a newsletter subscription that is initially set based on the newsletter I receive when the user logs in. However, when I toggle the newsletter option, I receive a "successfully updated" message but the newsletter remains set to false even ...

Is there a way to include a message in browser.wait() without altering the preset timeout value?

I have encountered an issue with my code: browser.wait(ExpectedConditions.presenceOf(elementName)); Unfortunately, this often fails and only provides the vague message "expected true to be false", which is quite frustrating. When it fails, I need a more ...

Ensure that the string functions as the primary interface

I'm working with an interface that looks like this interface Cat { color: string, weight: number, cute: Boolean, // even though all cats are cute! } Currently, I need to accomplish something similar to this const kitten: Cat = ... Object. ...

What is the best way to access data from a local JSON file in Gatsby when using TypeScript and GraphQL?

I'm updating my former gatsby site using TypeScript. I encountered an issue while trying to retrieve data from a local JSON file: There appears to be an error in your GraphQL query: Cannot find field "allNavigationLinksJson" on type "Q ...

Error detected in Vuetify project's tsconfig.json file - The configuration file does not contain any input entries

Having an issue with the tsconfig.json file in my Vuetify project. The first { is underlined in red and when hovered over, it displays the error message: No inputs were found in config file Sharing the content of the file below. tsconfig.json { // expe ...

Encountering issues in transmitting form data to a Node server from Angular

In my Angular application, I am working on creating a registration page with validation. Once the user fills out the form and submits it, the data is sent to the server and saved in MongoDB. This is the approach I have taken: register_user() { const ...

Creating a custom styled-component in Typescript for rendering {props.children}

One of my components is called ExternalLink which is used to display anchor tags for external URLs. This component has been styled using styled-components. Check out the CodeSandbox demo here ExternalLink.tsx This component takes an href prop and render ...

Preventing the conversion of literals to classes in TypeScript

Using TypeScript, you can convert object literals to a class by doing the following: let businessObj = new ScenarioController(<FormatService>{ format: x => x }); Is there a way to prevent these types of casts in the compiler or linter? Oft ...

Next.js is having trouble identifying the module '@types/react'

Every time I attempt to launch my Next.js app using npm run dev, an error notification pops up indicating that the necessary packages for running Next with Typescript are missing: To resolve this issue, kindly install @types/react by executing: np ...

Having difficulty converting string columns/data to numerical values when exporting an Ag-Grid table to Excel with getDataAsExcel function

I am having an issue with exporting data from my ag-grid table to excel using the API method getDataAsExcel. The problem arises because I have a column containing only string values, while all other columns are numeric and displayed in the ag-grid table a ...

Show detailed information in a table cell containing various arrays using AngularJS

After integrating d3.js into my code, I now have an array with key-value pairs. Each team is assigned a key and its corresponding cost is the value. When I check the console log, it looks like this: Console.log for key and value Rate for current month [{ ...