Simplify complex type by merging intersections and eliminating repeated union keys

Although the question title and code below may seem unnecessary, I am struggling to find a clearer way to phrase my inquiry.

UPDATE: Simplified code snippet:

type MyUnion = "a" | "b";

type Result = {
  [K in MyUnion]: Record<`${K}_1`, { v: `${K}_2` }>;
};
// type Result = { a: Record<"a_1", { v: "a_2"; }>; b: Record<"b_1", { v: "b_2"; }>; };
type DesiredResult = { "a_1": { v: "a_2" }; "b_1": { v: "b_2" }; };

I'm having trouble figuring out how to derive DesiredResult from Result

Answer №1

Seeking to achieve Flatten<T> as the intersection of all properties in T, one can utilize a method akin to UnionToIntersection illustrated in Transform union type to intersection type. By placing types to intersect in a contravariant position and leveraging infer, we can infer a single type:

type Flatten<T extends object> = { [K in keyof T]: (x: T[K]) => void } extends
    Record<keyof T, (x: infer I) => void> ? I : never;

This transforms, for example,

{a: {x: string}, b: {y: number}, c: {z: boolean}}
into
{x: string} & {y: number} & {z: boolean}
. To avoid a large intersection, consider combining it using a domain-like mapped type:

type Flatten<T extends object> = { [K in keyof T]: (x: T[K]) => void } extends
    Record<keyof T, (x: infer I) => void> ? { [K in keyof I]: I[K] } : never;

The result would be

{x: string; y: number; z: boolean}
.

Let's put it to the test:

type Result = {
    a: Record<"a_1", {
        v: "a_2";
    }>;
    b: Record<"b_1", {
        v: "b_2";
    }>;
}

type DesiredResult = Flatten<Result>;
/* type DesiredResult = {
    a_1: {
        v: "a_2";
    };
    b_1: {
        v: "b_2";
    };
} */

Validation successful.


Note that you could use UnionToIntersection directly like this:

type UnionToIntersection<U> =
    (U extends any ? (x: U) => void : never) extends
    ((x: infer I) => void) ? I : never

type Flatten<T extends object> = UnionToIntersection<T[keyof T]>

To achieve practically the same result:

type DesiredResult = Flatten<Result>;
/* type DesiredResult = Record<"a_1", { v: "a_2"; }> & Record<"b_1", { v: "b_2"; }> */

However, if any of the type's properties are unions themselves, they'll also end up being intersected. For instance, with

type X = Flatten<{ a: { x: string } | { y: number }, b: { z: boolean } }>;

The expected output would look like

/* type X = { x: string; z: boolean; } | { y: number; z: boolean; } */

But using UnionToIntersection would lead to

/* type X = { x: string; } & { y: number; } & { z: boolean; } */

Playground link to 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

Error: Parsing error - Unrecognized character "@" in Angular module

Currently I am delving into the realm of webpack and attempting to integrate it into my project. However, I seem to have hit a roadblock as I encounter the following error. Despite my efforts to troubleshoot and research, I cannot seem to find a loader spe ...

Tips for resolving the "trim" of undefined property error in Node.js

Looking to set up a basic WebAPI using Firebase cloud functions with express and TypeScript. Here's the code I have so far: import * as functions from 'firebase-functions'; import * as express from 'express'; const app = express( ...

What is the best way to set up an empty constructor with a nested object?

Currently struggling with this issue in my code: I am unable to correctly initialize the nestedStudents property. Every attempt results in an error suggesting the object may be undefined. export class StudentDto { students: number; nestedStudents: { ...

Unraveling the complexities of Typescript's Advanced Type NonFunctionPropertyNames

Delving deeper into the realm of advanced types in Typescript, I came across an intriguing type called NonFunctionPropertyNames. This type is designed to extract only the properties of a given object that are not functions. type NonFunctionPropertyNames&l ...

How can Material UI React handle long strings in menu text wrapping for both mobile and desktop devices?

Is there a way to ensure that long strings in an MUI Select component do not exceed the width and get cut off on mobile devices? How can I add a text-wrap or similar feature? Desktop: https://i.sstatic.net/lo8zM.png Mobile: https://i.sstatic.net/8xoW6. ...

Constructor not executing when using Object.create

Attempting to instantiate a class within a static method, I am using Object.create(this.prototype), which appears to be functioning correctly. Nonetheless, when I check the console, my property items is showing as undefined. The base class called model lo ...

Skipping necessary module in TypeScript

There are times when I struggle to locate updated type definition files for a new version of a node package I am working with. For instance, I am facing difficulty in finding a recent type definition file for Mongoose. As a result, I encounter errors when ...

How to Include HttpClient in an Angular Service

Looking for a service that utilizes http requests? import { Injectable } from '@angular/core'; import { Observable, of } from 'rxjs'; import { HttpClient } from '@angular/common/http'; @Injectable({ providedIn: 'root&a ...

Generic TypeScript any object that is plain

update I have included my current code to better illustrate the problem: https://gist.github.com/LukasBombach/7bf255392074509147a250b448388518 Using TypeScript, I aim to define a generic that represents any plain object data structure class MyClass<T ...

Images with spaces in their names are failing to load in React Native

I am currently trying to utilize an image within my react native application. At this point, my code is very minimal. The following code snippet is all I have for now: import React from 'react'; import { ScrollView, View, Text } from 'reac ...

What is the most efficient way to execute useEffect when only one specific dependency changes among multiple dependencies?

My main objective is to update a state array only when a specific state (loadingStatus) undergoes a change. Yet, if I include solely loadingStatus as a dependency, React throws an error requesting all dependencies [loadingStatus, message, messageArray, set ...

Application built with Electron and Typescript encounters module-related crash

While working on developing a client using Electron with Typescript, I encountered the following error: https://i.sstatic.net/7qLGh.png The configuration in tsconfig.json looks like this: { "compilerOptions": { "target": "e ...

Initial state was not properly set for the reducer in TypeScript

Encountered an error while setting up the reuder: /Users/Lxinyang/Desktop/angular/breakdown/ui/app/src/reducers/filters.spec.ts (12,9): error TS2345: Argument of type '{}' is not assignable to parameter of type '{ selectionState: { source: ...

The type '{} is not compatible with the type 'IProps'

In my current project, I am utilizing React alongside Formik and TypeScript. The code snippet below demonstrates my usage of the withFormik Higher Order Component (HOC) in my forms: import React from 'react'; // Libraries import........ import { ...

The repository injected into NestJs using TypeORM suddenly becomes null

In my code, I am fetching Requisition data from an external system using the following approach: init() { const requisitionData = this.loginMb().pipe( map(response => response.data.token), switchMap(loginData => this.getRequisitions(loginD ...

Tips for displaying personalized data with MUI DatePicker

I need to create a React TypeScript component that displays a MUI DatePicker. When a new date is selected, I want a custom component (called <Badge>) to appear in the value field. Previously, I was able to achieve this with MUI Select: return ( ...

When using TypeScript, the reducer function may not be recognized, causing the type to display as 'any

I am a beginner in Typescript and we are implementing hooks in our React application. We have a shared thunk action creator that triggers one of the actions. appSlice.ts type ThunkOptions = { method: number, api_url: string, body: any | null } ...

The formio onchange event may result in an undefined object

Encountering an issue here: Error: src/app/app.component.html:1:30 - error TS2532: Object is possibly 'undefined'. 1 <form-builder [form]="form" (change)="onChange($event)"></form-builder> while working on my for ...

Tips on creating a Jasmine unit test scenario for a function that triggers onerror of an image tag

I am facing an issue with the code below as the test case is failing //test case it('should display default image if the image link in people finder result does not exist', fakeAsync(() => { debugger component.resultsItem = M ...

The current version of Firebase functions is not reflecting the most recent modifications when running "firebase serve"

Exploring firebase functions has been a fun journey for me. Everything works smoothly when I deploy to the firebase server using the command firebase deploy --only functions. However, I wanted to test my functions locally before deploying them, and encount ...