25043

Is there a way to prevent union types in TypeScript?

Question:

I'm new to conditional types, so I tried the most obvious static way, no success:

type NoUnion<Key> = Key extends 'a' ? 'a' : Key extends 'b' ? 'b' : never; type B = NoUnion<'a'|'b'>;

The B type is still a union. Would somebody please school me?

Here's a <a href="http://www.typescriptlang.org/play/#src=type%20NoUnion%3CKey%3E%20%3D%0A%20%20Key%20extends%20'a'%20%3F%20'a'%20%3A%0A%20%20Key%20extends%20'b'%20%3F%20'b'%20%3A%0A%20%20never%3B%0A%0Atype%20B%20%3D%20NoUnion%3C'a'%7C'b'%3E%3B" rel="nofollow">playground</a>.

Answer1:

I am unsure what the usecase for this is, but we can force the NoUnion to never if the passed type is a union type.

As other mentioned conditional types distribute over a union, this is called <a href="https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html" rel="nofollow">distributive conditional types</a>

<blockquote>

Conditional types in which the checked type is a naked type parameter are called distributive conditional types. Distributive conditional types are automatically distributed over union types during instantiation. For example, an instantiation of T extends U ? X : Y with the type argument A | B | C for T is resolved as (A extends U ? X : Y) | (B extends U ? X : Y) | (C extends U ? X : Y).

</blockquote>

The key there is 'naked type', if we wrap the type in a tuple type for example the conditional type will no longer be distributive.

type UnionToIntersection<U> = (U extends any ? (k: U)=>void : never) extends ((k: infer I)=>void) ? I : never type NoUnion<Key> = // If this is a simple type UnionToIntersection<Key> will be the same type, otherwise it will an intersection of all types in the union and probably will not extend `Key` [Key] extends [UnionToIntersection<Key>] ? Key : never; type A = NoUnion<'a'|'b'>; // never type B = NoUnion<'a'>; // a type OtherUnion = NoUnion<string | number>; // never type OtherType = NoUnion<number>; // number type OtherBoolean = NoUnion<boolean>; // never since boolean is just true|false

The last example is an issue, since boolean is seen by the compiler as true|false, NoUnion<boolean> will actually be never. Without more details of what exactly you are trying to achieve it is difficult to know if this is a deal breaker, but it could be solved by treating boolean as a special case:

type NoUnion<Key> = [Key] extends [boolean] ? boolean : [Key] extends [UnionToIntersection<Key>] ? Key : never;

Note: UnionToIntersection is taken from <a href="https://stackoverflow.com/questions/50374908/transform-union-type-to-intersection-type" rel="nofollow">here</a>

Answer2:

By the way, the "simpler" one I was trying to come up with looks like this:

type NotAUnion<T> = [T] extends [infer U] ? U extends any ? [T] extends [U] ? T : never : never : never;

This should work (please test it; not sure why I got the original version in <a href="https://stackoverflow.com/a/50611240/2887218" rel="nofollow">my answer to another question</a> wrong but it's fixed now ). It's a similar idea to the UnionToIntersection: you want to make sure that a type T is assignable to each part of T if you distribute it. In general that's only true if T is a union with just one constituent part (which is also called "not a union").

Anyway, @TitianCernicovaDragomir's answer is perfectly fine also. Just wanted to get this version out there. Cheers.

Recommend

  • Android “You need to use a Theme.AppCompat theme (or descendant) with the design library”
  • How can I implement a JavaScript that submit this form according to the clicket button? [duplicate]
  • Take picture automatically with no user interaction [closed]
  • Connecting Node.js app on Google Cloud App Engine to a Google Cloud SQL instance
  • How to get javascript output in python BeautifulSoup or any other module
  • Famo.us swipe on scrollview
  • java.lang.String cannot be cast to java.lang.Long in Spring Security ACL
  • How to make a Label appear then disappear after a certain amount of time in python tkinter
  • Java Generics - When to use “T” and “?” [closed]
  • How to align elements in cells of a css grid?
  • How to get mouseover event from android emulator
  • How to implement NOT LIKE as the search condition for containstable(Full-Text Query)?
  • JSF 2 OpenJPA 2 Glassfish 3.1 WEB9031 Error
  • How to wrap string in span before and after all newlines in PHP?
  • Add chapter information to existing video with AVFoundation
  • Apply function to an array of tuples
  • UITextField get focus and then lose focus immediately due to return YES in textFieldShouldReturn
  • How to access a bundled ES6 class in inline
  • Stop the background service after particular time in android
  • Sending in an object of type Object instead of String - Polymorphism
  • JQuery and PHP validation problem?
  • How should I structure my query string in MVC htaccess applications?
  • Installation failed for laravel/lumen-installer: guzzlehttp/guzzle locked at 6.3.0
  • Covert RFC3339 DateTime to Date in java [duplicate]
  • Retrieve 3rd MAX salary in Hive
  • MySql tables, error #1064 & error #1068 Multiple primary key defined [closed]
  • Redmine can't generate secret token
  • why 'read' command in shell script is missing initial characters? [duplicate]
  • On unchecking a checkbox trigger an event
  • create circular Auto Horizontal Scroll View?
  • internal javascript not works in angular2
  • Defer unused CSS
  • Create .java file and compile it to a .class file at runtime
  • Issue with Terrain Collision using Three.js
  • JQuery Mobile Ajax Navigation in Single-Page Template
  • Set SelectedIndex of ListView in FlipView_SelectionChanged event
  • All Event listing on specified date in Google Calender api (V3) in java?
  • Creating 2d platforms using JavaScript
  • What does the “id” field in an Android “Google Play Music” broadcast intent correspond to?
  • ReferenceError: TextEncoder is not defined