Only map property type when the type is a union type

My goal is to create a mapping between the keys of an object type and another object, following these rules:

  • Each key should be from the original type
  • If the value's type is a union type, it should have { enum: Array }
  • If the value's type is not a union type, it should have { enum: never }

For example:

type OriginalType = {
    foo: "a" | "b";
    bar: "c";
    foobar: Record<string, string>;
};

type MappedType<T> = { ... };

type ResultType = MappedType<OriginalType>;

// ResultType = {
//    foo: { enum: ["a", "b"] },
//    bar: { enum: never },
//    foobar: { enum: never },
//}

I am able to map the keys and values to a new type, but I'm struggling with determining whether a type is a union type:

type IsUnion<T> = ...

type MappedType<T> = {
    [K in keyof T]: IsUnion<T[K]> extends true ? { enum: Array<T[K]> } : never;
};

Answer №1

When working with TypeScript, it can be challenging to distinguish unions from non-unions directly. Therefore, any custom implementation of this functionality may encounter unexpected edge cases. One way to detect a union type is by utilizing distributive conditional types to divide a type into its union members and compare each member with the original unsplit type. If the type is a union, each piece will differ from the whole, while a non-union type will have only one identical piece. Here's an example:

type IsUnion<T, U = T> = 
  T extends unknown ? [U] extends [T] ? false : true : never;

In the above code snippet, I've demonstrated how default generic type arguments can be used to verify whether a given type is a union or not. Testing this out:

type U = IsUnion<number>; // false
type V = IsUnion<string | number>; // true

The results seem accurate. For a practical example, consider applying this concept to map certain properties in a type based on whether they are part of a union or not:

type MappedType<T> = {
  [K in keyof T]: IsUnion<T[K]> extends true ? { enum: Array<T[K]> } : never;
};
// Further usage and results here...

This approach allows for dynamic checking and mapping of types within TypeScript programs. You can experiment further with the provided Playground link.

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 concept of inheritance and nested views within AngularJS

I've encountered a challenge while setting up nested views in AngularJS. Utilizing the ui-router library has been beneficial, but I'm facing issues with separate controllers for each view without proper inheritance between them. This results in h ...

Flashing Screens with Ionic 2

I am currently dealing with a situation where my login page and homepage are involved. I have implemented native storage to set an item that helps in checking if the user is already logged in (either through Facebook or Google authentication). The logic fo ...

Can the Date class be expanded by overloading the constructor method?

In my dataset, there are dates in different formats that Typescript doesn't recognize. To address this issue, I developed a "safeDateParse" function to handle extended conversions and modified the Date.parse() method accordingly. /** Custom overload ...

What are the repercussions of labeling a function, TypeScript interface, or TypeScript type with export but never actually importing it? Is this considered poor practice or is there a potential consequence?

I find myself grappling with a seemingly straightforward question that surprisingly has not been asked before by others. I am currently immersed in a TypeScript project involving Vue, and one of the developers has taken to labeling numerous interfaces and ...

When initiating an Ionic project, you may notice a repeated message in the terminal saying, "[INFO] Waiting for connectivity with npm..."

I'm in the process of setting up a new Ionic project along with all the necessary dependencies. However, whenever I try to execute the command "ionic serve," I keep getting stuck at the continuous display of the message "[INFO] Waiting for connectivit ...

Managing the onChange event for a MaterialUI dropdown component

I'm encountering an issue with validating the MaterialUI TextField component (Country list) wrapped with Autocomplete. I am trying to use the onChange event to enable the Submit button when the country field is filled in. However, the problem arises w ...

A Guide to Implementing Inner CSS in Angular

I am working with an object named "Content" that has two properties: Content:{ html:string; css:string } My task is to render a div based on this object. I can easily render the html using the following code: <div [innnerHtml]="Content.html"& ...

Validating Inputs with an Array of Values in my Angular 2 Application

I have been exploring ways to retrieve data from an array of values instead of a single value when using [(ngModel)] binding in my Angular 2 application. The current setup functions perfectly with a single value, as shown below. The data is pulled from the ...

Mastering Angular Apollo Error Resolution Methods

Hey everyone, I'm facing a problem with apollo-angular and apollo-link-error that I just can't seem to figure out. I've experimented with different approaches but have had no luck catching errors on the client-side of my angular web app. Bel ...

How to Measure the Length of an Undefined Value in Jasmine Angular Unit Tests

Here's a function that I have: updateParts(enviromentContainsAllParts: PartsContainsAllParts): Observable<boolean> { const enviroment = cloneDeep(this.enviroment); enviroment.containsAllPart = enviromentContainsAllParts.containsAllPart ...

Learn how to utilize the combineLatest/zip operators to only respond to emissions from the second observable while disregarding emissions from the first observable

Here's an example of how I'm initializing a property: this.currentMapObject$ = zip(this.mapObjects$, this.currentMapObjectsIndex$, (mapObjects, index) => mapObjects[index]); I want the value of this.currentMapObject$ to be emitted only ...

When faced with the error message "Typescript property does not exist on union type" it becomes difficult to assess the variable

This question is a continuation of the previous discussion on Typescript property does not exist on union type. One solution suggested was to utilize the in operator to evaluate objects within the union. Here's an example: type Obj1 = { message: stri ...

What is the best way to retrieve the current complete URL in a Next.js/Typescript component?

I'm working on a component and I need to retrieve the current full URL. Here's a simplified version of what I have: /** * Share dropdown component */ export const ShareDropdown: React.FC<{ className: string }> = ({ className, }) => { ...

The React useEffect() hook causing an infinite re-render when trying to fetch all data regardless of

Recently, I've begun diving into React and utilizing the useEffect hook to fetch news and events from a database upon page load. However, when attempting to add a loading spinner, I encountered an unexpected infinite loop issue that has left me scratc ...

Whenever I try to utilize async with React.FC in my React component, a TypeScript error is thrown

I am currently working on a React functional component called DashboardPage that utilizes async/await for fetching data, but I am running into a TypeScript error. The specific error message reads: 'Type '({ params }: DashboardPageProps) => Pro ...

Customizing Carousel Arrows in Angular with ng-bootstrap

I need help changing the position and icon of control arrows using Bootstrap. I've tried targeting "carousel-control-prev-icon" & "carousel-control-next-icon", but nothing seems to work. Any suggestions on how to properly solve this issue? Here is th ...

In Angular, additional code blocks are executed following the subscription

I am facing an issue with my file upload function. After the file is uploaded, it returns the uploaded path which I then pass to a TinyURL function this.tinyUrl.shorten(data.url).subscribe(sUrl => { shortUrl=sUrl;});. However, there is a delay in receiv ...

Utilizing the spread operator in Typescript interfaces: best practices

I have a react component that includes the spread operator operating on ...other and passed down to lower levels of the component. interface ButtonProps { colourMode: string; regular: boolean; buttonText: string; disabled?: boolean; iconSize?: st ...

Issues have been encountered with Angular 5 when trying to make required form fields work properly

Just created my first Angular app using Angular 5! Currently following the documentation at: https://angular.io/guide/form-validation. Below is the form I have set up: <form class="form-container"> <mat-form-field> <input matInput pl ...

Calculate the date input in Angular 7 by subtracting calendar days

As a user of this Angular 7 application, you are required to choose a date from a calendar input and then determine what day it was 40 days ago. Unfortunately, I have not been able to accomplish this in Typescript yet. There are numerous JavaScript solutio ...