Looking for a type that combines multiple options

I am interested in incorporating union types as function arguments, with the ability to utilize the arguments and have any missing ones default to undefined.

However, when it comes to 'name' and 'age', there seems to be a type conflict.

function sample(props: { id: number } & ({ name: string } | { age: number })) { 
  const { id, name, age } = props
}

This is what I envision:

sample({ id: 1, name: "Tom" })

sample({ id: 1, age: 31 })

Answer №1

A slightly modified version of StrictUnion as detailed in the post here seems to be effective:

type UnionKeys<T> = T extends T? keyof T : never;
type StrictUnionHelper<T, TAll> = T extends T? T & Partial<Record<Exclude<UnionKeys<TAll>, keyof T>, undefined>> : never;
type StrictUnion<T> = StrictUnionHelper<T, T>

function example(props: StrictUnion<{ id: number } & ({ name: string } | { age: number })>) { 
  const { id, name, age } = props
}

Playground Link

The concept behind StrictUnion is to ensure all elements within a union have every member from each element. This is accomplished by filling in missing members with type undefined. Thus, a type like

{ id: number } & ({ name: string } | { age: number })
transforms into
{ id: number; name: string; age: undefined }
or
{ id: number; name: undefined; age: number }
, allowing for easy de-structuring.

To create StrictUnion, we first collect keys from all union constituents using conditional types' distribution behavior. By leveraging an always true condition (such as T extends T, T extends unknown, or less favorably, T extends any), we can compile a type that extracts keys from each constituent and consolidates them. The key extraction process looks like this:

type UnionKeys<T> = T extends T ? keyof T : never;

This application of the type is exemplified below:

type A = { id: number; name: string }
type B = { id: number; age: number }

UnionKeys<A | B>
  // Conditional type applied to A and B and the results combined
  <=> (A extends unknown ? keyof A: never) | (B extends unknown ? keyof B: never) 
  <=> keyof A | keyof B
  <=> ("id" | "name") | ("id" | "age")
  <=> "id" | "name" | "age"

Once we have UnionKeys, another distributive conditional type inspects each union member to identify missing keys from a given type T (using

Exclude<UnionKeys<TAll>, keyof T>
). This process entails intersecting the original T with a Partial Record featuring these absent keys typed as undefined. The union must be supplied twice: once for distribution (T) and again to gather all keys through UnionKeys.

Below is how this type is utilized:

type A = { id: number; name: string }
type B = { id: number; age: number }
StrictUnion<A | B>
  <=> StrictUnionHelper <A | B, A | B>
  // Distributes over T
  <=> (A extends A ? A & Partial<Record<Exclude<UnionKeys<A | B>, keyof A>, undefined>> : never) | (B extends B ? B & Partial<Record<Exclude<UnionKeys<A | B>, keyof B>, undefined>>> : never)
  <=> (A extends A ? A & Partial<Record<Exclude<"id" | "name" | "age", "id" | "name">, undefined>> : never) | (B extends B ? B & Partial<Record<Exclude<"id" | "name" | "age", "id" | "age">, undefined>>> : never)
  <=> (A extends A ? A & Partial<Record<"age", undefined>> : never) | (B extends B ? B & Partial < Record < "name" >, undefined >>> : never)
  // With both conditions evaluating to true, the intersection is carried out successfully
  <=> { id: number; name: string; age?: undefined } | { id: number; age: number; name?: undefined }

Answer №2

An effective approach could be to set the "undefined" arguments as default values.

function example(props: { id: number } & ({ name: string } | { age: number })) { 
  const available = { ...{ name: undefined, age: undefined }, ...props }

}

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

Angular failing to retrieve URL parameters correctly

As I was trying to retrieve URL queries like www.website.com?a:b, I decided to follow the guidance provided in a particular Angular tutorial. This official tutorial (accessible via this link) instructed me to implement the following simple code snippet wit ...

Changing the generic type's constraint type in TypeScript to have more flexibility

I have developed a utility type named DataType that takes in a parameter T restricted to the type keyof MyObject. When the key exists in MyObject, DataType will return the property type from MyObject; otherwise, it will simply return T. interface MyObject ...

Having difficulty transferring navigation props between screens using react-navigation

Within my ContactList component, I have utilized a map to render various items. Each item includes a thumbnail and the desired functionality is that upon clicking on the thumbnail, the user should be directed to a new screen referred to as UserDetailsScree ...

TS2688 Error: TypeScript Build Fails to Locate Type Definition File for 'mocha' Following Update

After updating my TypeScript to the latest version, I keep encountering the following error: Cannot find type definition file for 'mocha'. tsconfig.json { "compilerOptions": { "emitDecoratorMetadata": true, "experimentalDecorators ...

react-query: QueryOptions not functioning as expected when utilizing userQueries()

When passing certain "query options" while using useQueries() to fetch multiple queries simultaneously, these specified "query options" do not get applied during query executions (e.g. refetchOnWindowFocus has a value of true but I want it to be false). F ...

Expanding the capabilities of Hapi types in Typescript

I'm in the process of enhancing existing hapi definitions by incorporating a new type, specifically the seneca type. Here's an example: interface SenecaMethods { act: any; add: any; } interface HapiServer extends Hapi.Server { info: ...

Python Error: TypeError - Trying to add an integer and a function. Struggling to manipulate the returned value?

As I dive into the world of coding, my current project is a personal quiz designed to assist in learning and memorizing German vocabulary. However, one major roadblock stands in my way - how do I make this quiz grade itself? Take a look at the code snippet ...

Dynamically loading external JavaScript for an Angular component and triggering the window load event

I am currently dealing with an external javascript file that I only want to be included on a specific component, so the approach I'm taking involves dynamically loading it. I came across this answer that explains exactly how to achieve this. The prob ...

Comparing plain objects and class instances for modeling data objects

What is the recommended approach for creating model objects in Angular using TypeScript? Is it advisable to use type annotation with object notation (where objects are plain instances of Object)? For example, let m: MyModel = { name: 'foo' } ...

Utilizing Typescript Decorators to dynamically assign instance fields within a class for internal use

I am interested in delving into Typescript Decorators to enhance my coding skills. My primary objective is to emulate the functionality of @Slf4J from Project Lombok in Java using Typescript. The concept involves annotating/decorating a class with somethin ...

Steps to resolve the 'Cannot assign value to userInfo$ property of [object Object] that only has getter' issue in Angular

I am currently in the process of building a web application using NGXS, and I'm encountering a specific error that I'm trying to troubleshoot. The issue arises when I attempt to fetch data from an API and display it within a column on the page. D ...

Changing over to the left hand coordinate systemUniquely converting to a

One of the challenges I am facing involves an application that loads a model. Upon receiving a server response, I am provided with information regarding the axis to use for the right and up orientation. The format in which this information is sent can be ...

Transform Typescript code into Javascript using webpack while preserving the folder organization

Is there a way for webpack to compile my typescript node project into js while preserving the directory structure and not bundling into one file? This is my current project structure: src |_controllers |_home |_index.ts |_ services ...

What is the best way to set wrapper props without hiding it from showing up in attrs?

Creating a Wrapper Component with Specific Props <script setup lang="ts"> import InputBase,{ Props as InputBaseProps } from "./InputBase.vue"; interface Props extends InputBaseProps { label?: string; labelClassName?: string; ...

Restrictions on pairings of kind variables within generic utilization

Currently, I am creating a declaration file for a library called chart.js. The process of constructing a new chart involves the following: let chart = new Chart(ctx, { type: 'line', data: ..., options: ... }) The types of the data and options f ...

How to conditionally apply a directive to the same tag in Angular 4

I am implementing angular 4 and have a directive in my template for validation purposes. However, I would like to first check if a specific condition is true before applying the directive. Currently, my code looks like this: <div *ngIf="groupCheck; els ...

Error message thrown by node express.js indicating that response headers cannot be reset once they have been sent

As a newcomer to both node and express, I may be making a silly mistake. If you want to see the complete source code, please visit: https://github.com/wa1gon/aclogGate/tree/master/server logRouter.get("/loggate/v1/listall", function(req, res) { let ...

encountering an error of unsupported grant type while attempting to authenticate a user

I've seen a lot of discussions on this topic, but none have addressed my specific issue. Currently, I am working on an angular 5 application and trying to retrieve an authentication token by sending a post request to a server. Testing the connection ...

What is the best way to ensure that my mat-slide-toggle only changes when a specific condition is met?

I'm having an issue with a function that toggles a mat-slide-toggle. I need to modify this function to only toggle when the result is false. Currently, it toggles every time, regardless of the result being true or false. I want it to not toggle when t ...

"Encountering an error with the any type in the useLocation feature while using React Router version 6

https://i.sstatic.net/0YcS9.png What steps should I take to resolve this particular type of error issue? My attempt at passing a custom type did not yield successful results. ...