Exploring the depths of complex objects with the inclusion of optional parameters

I've been working on a custom object mapping function, but I've encountered an issue. I'm trying to preserve optional parameters as well.

export declare type DeepMap<Values, T> = {
    [K in keyof Values]: 
        Values[K] extends any[] 
            ? Values[K][number] extends object 
                   ? DeepMap<Values[K][number], T>[] | T | T[] : T | T[]
            : Values[K] extends object
            ? DeepMap<Values[K], T>
            : T;
};

The original type is defined as:

type Obj1 = {
  a: number;
  b: {
    a: number;

  };
  c?: {
    a: number;
  } 
}

My desired new type looks like this:

type MappedObj1 = {
  a: string;
  b: {
    a: string;

  };
  c?: {
    a: string;
  } 
}

However, currently I only have the following structure:

type MappedObj1 = {
  a: string;
  b: {
    a: string;

  };
}

I seem to have lost the optional type in the process. Can someone please advise me on how to map a deep object and retain all parameters with optionals?

Playground

Answer №1

After reviewing the provided code example, I believe that utilizing the following typing would be appropriate:

type DeepMap<T, V> =
    T extends undefined ? undefined :
    T extends object ? { [K in keyof T]: DeepMap<T[K], V> } :
    V;

This definition of DeepMap<T, V> will convert any non-undefined primitive into type V. For instances where the value is undefined, it will remain as such. When dealing with objects, including arrays, the typing performs a recursive application of DeepMap on its properties. By maintaining a homomorphic approach, it retains the optionality of the properties and ensures union preservation through distributive typing. An illustration of this type can be seen in MappedObj1:

type MappedObj1 = DeepMap<Obj1, string>;
/* type MappedObj1 = {
    a: string;
    b: {
        a: string;
    };
    c?: {
        a: string;
    } | undefined;
} */

A clarification regarding why undefined was specifically handled: It is important to note that optional properties encompass undefined within their value types. Hence, {c?: {a: number}} considers the same as {c?: {a: number} | undefined}. To ensure mapping consistency—where {c?: {a: number}} corresponds to {c?: {a: string} | undefined} rather than

{c?: {a: string} | string}</code—and uphold both homomorphic and distributive mappings, it is necessary for <code>undefined
to map to itself rather than another type like string.

Access the playground link for the code here.

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

Having difficulty executing the Cypress open command within a Next.js project that uses Typescript

I'm having trouble running cypress open in my Next.js project with Typescript. When I run the command, I encounter the following issues: % npm run cypress:open > [email protected] cypress:open > cypress open DevTools listening on ws: ...

Error TS2488 in React TypeScript: The data type 'IStateTypes' is required to have a method called '[Symbol.iterator]()' that returns an iterator

At the moment, I am working on implementing a global state in React Hooks but have run into an issue. https://i.stack.imgur.com/DN83K.png The current problem I'm facing is with [Symbol.iterator](. I am uncertain about how to resolve this as I am in ...

Retrieving the key from an object using an indexed signature in Typescript

I have a TypeScript module where I am importing a specific type and function: type Attributes = { [key: string]: number; }; function Fn<KeysOfAttributes extends string>(opts: { attributes: Attributes }): any { // ... } Unfortunately, I am unab ...

The service being injected is not defined

Two services are involved in this scenario, with the first service being injected into the second service like so: rule.service.ts @Injectable() export class RuleService { constructor( private _resourceService: ResourceService ){} s ...

Angular/NestJS user roles and authentication through JWT tokens

I am encountering difficulties in retrieving the user's role from the JWT token. It seems to be functioning properly for the ID but not for the role. Here is my guard: if (this.jwtService.isTokenExpired() || !this.authService.isAuthenticated()) { ...

Improving type definitions in Typescript 2.6 using module augmentation leads to error TS2339: Property '' is not found on type ''

Currently utilizing the material-ui library version v1.0.0-beta. An update was released yesterday to v1.0.0-beta.28, however, the type definitions were not updated resulting in runtime errors while compilation remains successful. Encountering this error i ...

Error in Firebase Functions: Promises must be properly managed

Currently, I am in the process of creating a Firebase function using TypeScript to deliver push notifications to multiple users. However, whenever I execute the command firebase deploy --only functions, TSLint flags an error stating "Promises must be han ...

Adjusting the audio length in React/Typescript: A simple guide

I'm currently developing a web app with React and TypeScript. One of the components I created is called SoundEffect, which plays an mp3 file based on the type of sound passed as a prop. interface ISoundEffectProps { soundType: string, // durat ...

Typescript having issues compiling to commonjs/es2015 accurately

I currently have Node v14.5.0 installed and I'm using ts-node-dev in my development environment However, I am encountering an error every time I try to compile to JS. Initially, I attempted with the following tsconfig: "target": "es5& ...

Guide on creating a custom command within the declaration of Tiptap while extending an existing extension with TypeScript

I'm currently working on extending a table extension from tiptap and incorporating an additional command. declare module '@tiptap/core' { interface Commands<ReturnType> { table: { setTableClassName: () => ReturnType; ...

Error 2322: Troubleshooting Typescript arrow functions overloads issues

Everything seems to be working well with the code below, except for an error that occurs with the resolve constant. const resolve: Resolve Type '(param: "case 1" | "case 2" | "case 3") => boolean | "string" | ...

What could be the reason for certain Angular modules importing successfully while others fail to do so?

I am encountering an issue with a module that I am struggling to import. Using Typescript 2.7 and Node 10 The pxl-ng-security module is showing an error in both VSCode and VS2019. When hovering over it, error 2307 is displayed. Below is the import secti ...

Update the function's argument type signature if the current argument is a function with properties

Looking for input on a potential title change, but for now, here are the details of my specific use case: I'm currently developing a library that facilitates executing methods remotely and accessing properties across serialized boundaries like those ...

Gathering adorned categorizations (sans any listed category divisions)

My current setup involves an event dispatcher class that triggers listeners on specified occurrences. I've successfully implemented registering event listeners via decorators, but I feel like there may be a better solution out there. At the moment, e ...

Retrieve functions contained within the component.ts file of an Angular library: tips and tricks

I have developed an Angular library, named 'mylib', where I have utilized only the mylib.component.ts file. The HTML element codes are included inside the template variable of this file, along with the functions responsible for modifying these el ...

ESLint is reminding you that the `parserOptions.project` setting must be configured to reference the tsconfig.json files specific to your

Within my NX Workspace, I am developing a NestJS-Angular project. Upon running nx lint, an error is triggered with the following message: Error: A lint rule requiring the TypeScript type-checker to be fully available has been attempted, but `parserOptions. ...

In order to emphasize the chosen list item following a component refresh

SCENARIO: Let's consider a scenario where I have a component named list, responsible for displaying a list of all customers. Within this list, certain conditions are set up as follows: 1) Initially, the 1st list-item (e.g. Customer 1) is selected by ...

Converting Promises to Observables

Struggling with the syntax as I delve into learning Angular, I need to transform a promise into an Observable. Let me share what I've encountered: In the function getCountries (subscribed by another utility), there is a call required to fetch a list ...

No data found in req.query object in ExpressJS

When I use http.post to send data from Angular to NodeJS, the req.query always comes back empty for me. Here is my server.js setup: const express = require('express'); const cors = require('cors'); const bodyParser = require('body ...

Angular 2 wrap-up: How to seamlessly transfer filter data from Filter Component to App Component

A filtering app has been created successfully, but there is a desire to separate the filtering functionality into its own component (filtering.component.ts) and pass the selected values back to the listing component (app.ts) using @Input and @Output functi ...