What is the best way to combine the attributes of multiple objects within a union type?

I have a clearly defined schema

type Schema = {
    a: { a: 1 }
    b: { b: 2 }
}

I am in need of a function that can generate objects that adhere to multiple schemas.

function createObject<K extends keyof Schema>(schema: Array<K>, obj: Schema[K]) {}

createObject(["a"], { a: 1 }) // works
createObject(["b"], { b: 2 }) // works
createObject(["a", "b"], { b: 2 }) // should error but it doesn't
createObject(["a", "b"], { a: 1, b: 2 }) // works

playground link

I have experimented with various approaches. It's intriguing that when you & a union with itself, the & is distributed across all items in the union and does not quite achieve the desired outcome. I am looking for a solution to operate on {a: 1} | {b: 2} in order to obtain {a: 1, b: 2}. Any suggestions?

Answer №1

If the types within the properties of your Schema are not unions themselves, you have the option to transform the union type Schema[K] into an intersection using conditional types. Here's an example:

type Schema = {
    a: { a: 1 }
    b: { b: 2 }
};    

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

function createObject<K extends keyof Schema>(
    schema: Array<K>, 
    obj: UnionToIntersection<Schema[K]>
) { }

createObject(["a"], { a: 1 }) // works
createObject(["b"], { b: 2 }) // works
createObject(["a", "b"], { b: 2 }) // error! 
createObject(["a", "b"], { a: 1, b: 2 }) // works

This approach may suit your needs. However, if you encounter a different scenario (e.g., if Schema includes a property like cd: {c: 3} | {d: 4} and you wish to retain a union in the final type), an alternative solution might be more suitable:

type PropsToIntersection<T, K extends keyof T> =
    { [P in K]: (k: T[P]) => void }[K] extends
    ((k: infer I) => void) ? I : never;

function createObject<K extends keyof Schema>(
    schema: Array<K>,
    obj: PropsToIntersection<Schema, K>
) { }

This second method involves iterating through the keys of Schema and then performing an intersection, instead of spreading the union of Schema[K]. The distinction is most noticeable in cases where some properties of your schema are union types themselves.

Hopefully, this explanation proves useful to you. Best of luck on your current project!

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

Exploring the functionalities of class methods within an Angular export function

Is it possible to utilize a method from an exported function defined in a class file? export function MSALInstanceFactory(): IPublicClientApplication { return new PublicClientApplication({ auth: AzureService.getConfiguration(), <-------- Com ...

Cloning a repository does not support Typescript compilation

After creating an Angular2 component, I wanted to share the code with my colleagues. Therefore, I uploaded the code to Github, cloned the repository, ran npm install, and then npm run tsc. However, I encountered the following errors: error TS2318: Cannot ...

Angular 2 forms are displaying ngvalid when input fields are marked as nginvalid

The form displays ngvalid because I have included the code like this: <form novalidate class="pop-form" (submit)="signUp()" #regForm="ngForm"> <div class="group"> <input type="text" [(ngModel)]="signUpData.name" [ngMode ...

Tips for choosing the node_modules distribution flavor to include in your webpack bundle

Issue: Following the update of AJV.js to Version 6.4, my vendor bundle now includes the "uri-js" ESNEXT version instead of the ES5 version, causing compatibility issues with IE11. Analysis: Upon investigation, I discovered that AJV references uri-js usi ...

Leverage the exported data from Highcharts Editor to create a fresh React chart

I am currently working on implementing the following workflow Create a chart using the Highcharts Editor tool Export the JSON object from the Editor that represents the chart Utilize the exported JSON to render a new chart After creating a chart through ...

I'm trying to figure out the best way to successfully pass a prop to another component in TypeScript without running into the frustrating issue of not being able to

I have been facing an issue while trying to pass a prop from a custom object I have defined. The structure of the object is as follows: export type CustomObjectType = { data?: DataObject }; export type DataObject = { id: number; name: stri ...

Debugger for Visual Code unable to locate URL in Microsoft Office Add-in

I recently installed the Microsoft Office Add-in Debugger VS code extension, but I'm having trouble getting it to work despite following the instructions. Every time I try, an error message pops up: https://i.sstatic.net/h2FYs.png Upon inspecting my ...

Running a Vue.js 3 application with TypeScript and Vite using docker: A step-by-step guide

I am currently facing challenges dockerizing a Vue.js 3 application using Vite and TypeScript. Below is my Dockerfile: FROM node:18.12.1-alpine3.16 AS build-stage WORKDIR /app COPY package.json ./ RUN yarn install COPY . . RUN yarn build-only FROM ngin ...

Navigating back to the login page in your Ionic V2 application can be achieved by utilizing the `this.nav

Running into an issue with navigating back to the login screen using Ionic V2. Started with the V2 tabs template but added a custom login page, setting rootPage = LoginPage; in app.components.ts. When the login promise is successful, I used this.nav.setR ...

Converting a nested JSON object into a specific structure using TypeScript

In the process of developing a React app with TypeScript, I encountered a challenging task involving formatting a complex nested JSON object into a specific structure. const data = { "aaa": { "aa": { "xxx&qu ...

What method can be used to verify if a username exists within Angular data?

We want to display online users on a webpage by checking if they are currently active. The current code logs all online users in the console, but we need to show this visually on the page. public isOnline: boolean = false; ... ... ngOnInit() { ...

In Angular, the data retrieved from the API will only appear on the page once it has been manually

Recently, I started working on an Angular project and encountered a problem with data display after each component routing. Initially, the data is not displayed until the page is reloaded. You can see the issue in the screenshots: https://i.sstatic.net/5My ...

Zod vow denial: ZodError consistently delivers an empty array

My goal is to validate data received from the backend following a specific TypeScript structure. export interface Booking { locationId: string; bookingId: number; spotId: string; from: string; to: string; status: "pending" | "con ...

Looking to execute a service method within an authguard service?

I am a beginner with Angular and I am looking to invoke a service method within the authguard service. The specific service function that I need is as follows. Please note that I do not want to make any changes to this service function. loadOrganizations() ...

Implementing reduce for filtering and mapping in TypeScript: A comprehensive guide

I encountered a problem with my code that I need help fixing. Here is a simple example: interface employer { name: string; age: number; } const arr: employer[] = [{name:'Amy',age:18},{name:'Bob',age:20}]; l ...

Struggling with an issue in React and Bootstrap4 while developing an application with create-react-app

In my experience with Windows 10, I encountered two scenarios where I faced problems. The first scenario involved creating applications using the create-react-app command and installing it with npm i -g [email protected]. Scenario 1 I stopped the React s ...

Removing data from a table using an identifier in Typescript

Recently, I have made the switch from using Javascript to TypeScript. However, I am facing an issue while trying to delete data from a table in my code. Whenever I attempt to delete data, I encounter this error message: Property 'id' does not e ...

What is the process of transforming a basic JavaScript function into a TypeScript function?

As a Java developer diving into TypeScript for frontend development, I've encountered a simple JavaScript code snippet that I'd like to convert to TypeScript. The original JavaScript code is: let numbers = [123, 234, 345, 456, 567]; let names = ...

What is the most effective method for merging two arrays in JavaScript?

Can a function be created to generate an array like the following? ["0-AA", "0-BB", "1-AA", "1-BB", "2-AA", "2-BB", "3-AA", "3-BB"] This particular function combines two array ...

Data from graphql is not being received in Next.js

I decided to replicate reddit using Next.js and incorporating stepzen for graphql integration. I have successfully directed it to a specific page based on the slug, but unfortunately, I am facing an issue with retrieving the post information. import { use ...