Extract values from a deeply nested object while retaining the type information

How can I map all object values of the first obj while preserving the generic type for the Wrapped object?

I attempted this using a mapped type, but encountered two issues:

  1. I'm struggling to represent a nested Object in the MapToWrappedType
  2. I can't seem to find a way to specify the type so that the mapped type is not
    Wrapper<ValueObject<...>>
    , but rather Wrapper<...>

Transforming from:

{
 a: ValueObject<number>,
 b: ValueObject<number>,
 c: {
   d: ValueObject<string>,
   e: ValueObject<number>
 }
}

To:

{
 a: Wrapper<number>,
 b: Wrapper<number>,
 c: {
   d: Wrapper<string>,
   e: Wrapper<number>
 }
}
class ValueObject<T> {
  public value: T;
  constructor(value: T) {
    this.value = value;
  }
}

class Wrapper<T> {
  public value: T;
  constructor(vo: ValueObject<T>) {
    this.value = vo.value;
  }
}

const obj = {
  a: new ValueObject<number>(1),
  b: new ValueObject<number>(2),
  c: {
    d: new ValueObject<string>("3"),
    e: new ValueObject<number>(4)
  }
} as const;

// 2 Problems:
// - how to express a nested object?
// - how to change the type to Wrapper<...> instead of Wrapper<Valueobject<...>>?
type MapToWrapped<T> = {
  [K in keyof T]: Wrapper<T[K]> 
}

function toWrappedObj<T>(obj: T): MapToWrapped<T> {
  const result: any = {};
  for (const key in obj) {
    const item = obj[key];
    if (item instanceof ValueObject) {
      result[key] = new Wrapper<any>(item.value);
    } else {
      result[key] = toWrappedObj(item);
    }
  }
  return result;
}

const wrappedValuesObj = toWrappedObj(obj);
const x = wrappedValuesObj.a // should be Wrapper<number>

CodeSandbox

Answer №1

To eliminate the need for the ValueObject and introduce the Wrapper, we must utilize mapped types and leverage the infer keyword.

An illustration of the infer concept as per the documentation:

type Flatten<Type> = Type extends Array<infer Item> ? Item : Type;

type Result = Flatten<Array<number>> // number

This example showcases extracting the type of the array item, which we can apply similarly to ValueObject.

Type extends ValueObject<infer Item> ? Item : Type;

Subsequently, wrapping the Item in Wrapper yields the intended outcome.

For nested levels, a recursive call to our mapper type is incurred if the mapped property isn't a ValueObject, assuming nested ValueObject instances aren't present. To enhance the readability of the result type, the Prettify utility type is used:

type Prettify<T> = T extends infer Result
  ? {
      [K in keyof Result]: Result[K];
    }
  : never;
type MapToWrapped<T> = Prettify<{
  [K in keyof T]: T[K] extends ValueObject<infer Value>
    ? Wrapper<Value>
    : MapToWrapped<T[K]>;
}>;

function toWrappedObj<T>(obj: T): MapToWrapped<T> {...}

Usage scenario:

const obj = {
  a: new ValueObject<number>(1),
  b: new ValueObject<number>(2),
  c: {
    d: new ValueObject<string>('3'),
    e: new ValueObject<number>(4),
  },
} as const;


// const wrappedValuesObj: {
//   readonly a: Wrapper<number>;
//   readonly b: Wrapper<number>;
//   readonly c: {
//       readonly d: Wrapper<string>;
//       readonly e: Wrapper<number>;
//   };
// }
const wrappedValuesObj = toWrappedObj(obj);
const x = wrappedValuesObj.a; // Wrapper<number>

Explore the playground

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

Tips for restricting User access and displaying specific sections of the menu

I have a component that utilizes map to display all menu parts. Is there a way to make certain parts of the menu hidden if the user's access rights are equal to 0? const Aside: React.FunctionComponent = () => { const[hasRight, setHasRight] = us ...

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 ...

Using the tensorflow library with vite

Greetings and apologies for any inconvenience caused by my relatively trivial inquiries. I am currently navigating the introductory stages of delving into front-end development. Presently, I have initiated a hello-world vite app, which came to life throug ...

Sending selected IDs from the JSON data

In my project, there is a JSON file named "workers" which contains information about all the workers. I have created a select component to display the names of the workers like this: https://i.sstatic.net/0Glyf.png Currently, I am selecting some workers ...

The TypeScript compiler is generating node_modules and type declaration files in opposition to the guidelines outlined in the tsconfig.json file

For the past week, I've been trying to troubleshoot this issue and it has me completely puzzled. What's even more puzzling is that this app was compiling perfectly fine for months until this problem occurred seemingly out of nowhere without any c ...

Retrieve a list of all file names within a designated directory using Angular

I am working on my Angular app and I need to list all the file names inside the assets folder. To achieve this, I am planning to utilize the npm library called list-files-in-dir https://www.npmjs.com/package/list-files-in-dir Here is the service impleme ...

Creating dynamic checkboxes in Angular4 and binding them to an array of IDs

Hey there developers, I've been encountering an issue while trying to bind my dynamically generated checkboxes to an array in my project. In my users.template.html file, I have the following code snippet: <div *ngFor="let r of roles" class="checkb ...

Enhancing DOM Elements in a React Application Using TypeScript and Styled-Components with Click Event

I've been working on an app using React, Typescript, and styled components (still a beginner with typescript and styled components). I'm trying to create a simple click event that toggles between which of the two child components is visible insid ...

What is the reason behind prettier's insistence on prefixing my IIAFE with ";"?

I've encountered async functions in my useEffect hooks while working on a JavaScript project that I'm currently transitioning to TypeScript: (async ():Promise<void> => { const data = await fetchData() setData(data) })() Previously, ...

Navigating through pages: How can I retrieve the current page value for implementing next and previous functions in Angular 7?

Greetings, I am a new learner of Angular and currently working on custom pagination. However, I am facing difficulty in finding the current page for implementing the next and previous functions. Can anyone guide me on how to obtain the current page value? ...

What is the most effective way to retrieve data from a URL and process it using reactjs?

Looking to consume JSON data from a URL, here is an example of the JSON structure: { "results": [ ... ], "info": { ... } } I aim to display the fetched data as a component property. What is the most efficient way to achie ...

"How can we trigger a re-render of a React component once a promise is fulfilled? Is there a way to prevent rendering until the

Having attempted to make updates to a functional component that interfaces with an azure-devops-ui/Filter, I've encountered a situation where I am utilizing the azure-devops-extension-sdk to handle async responses. This component is intended to be use ...

Error: Trying to access a property that is not declared on an empty object

Using a fully patched Visual Studio 2013, I am integrating JQuery, JQueryUI, JSRender, and TypeScript into my project. However, I am encountering an error in the ts file: Property 'fadeDiv' does not exist on type '{}'. While I believ ...

Error encountered in Angular CLI: Attempting to access property 'value' of an undefined variable

I am encountering an issue while trying to retrieve the values of radio buttons and store them in a MySql database. The error message I receive is TypeError: Cannot read property 'value' of undefined. This project involves the use of Angular and ...

Tips on optimizing NextJS for proper integration with fetch requests and headers functionality

I'm currently working on a NextJS project and following the official tutorials. The tutorials demonstrate how to retrieve data from an API using an API-Key for authorization. However, I've run into a TypeScript compilation error: TS2769: No ove ...

Clearing error messages from a form using the reset button or after cancelling the form

I am having trouble removing the error outline around the input box and error messages displayed below it. When I cancel the form or click on the reset button, the input fields' content along with the error messages should be cleared. However, current ...

Troubles with implementing child routes in Angular 6

I'm having trouble getting the routing and child routing to work in my simple navigation for an angular 6 app. I've configured everything correctly, but it just doesn't seem to be working. Here is the structure of my app: └───src ...

Error: Unable to access the 'https' property as it is undefined

My nuxt app, which is built with Firebase and Vue, encounters an error when running the emulator. The error message states: TypeError: Cannot Find Property 'https' of undefined. This issue seems to be related to the https property in my index.ts ...

What causes the discrepancy in results between these two NodeJS/Typescript imports?

Within my NodeJS project, I have integrated typescript version 3.2 alongside express version 4.16 and @types/express version 4.16. My development is focused on using Typescript with the intention of transpiling it later on. The guidelines for @types/expre ...

using outlines for FontAwesome icons in React Native

I am struggling to use the fontAwesome + icon in the middle of a circle as one item. I have tried placing it inside a circle icon, but it doesn't seem to work properly. import IconFA from 'react-native-vector-icons/FontAwesome'; < ...