Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript extract and create Union as a subset of a Union

Tags:

typescript

I have the following Union type;

type MyUnionType = 'foo' | 'bar' | 'baz'

I would like to create a new Union MySubUnion as a subset;

type MySubUnion = 'foo' | 'bar'

I would like MySubUnion to be constrained to the values of its parent MyUnionType

type MySubUnion = 'foo' | 'bas' // => Error Type String 'b...
like image 909
Barris Avatar asked Dec 05 '18 16:12

Barris


3 Answers

Restricting a union to a subset of consituents is subtyping. In TypeScript, A extends B is a way of saying that A is a subtype of B. (This seems backwards to some people at some times; by removing elements from a union, you are making the type more specific, which is a subtype. The word "extends" might seem out of place, but that's what it is).

Unfortunately, you can't use extends to narrow type aliases the way you can with interfaces. What you'd like to do is use following invalid syntax:

// this is not valid TypeScript, do not use this:
type MySubUnion extends MyUnionType = 'foo' | 'bar'; // should work
type MySubUnion extends MyUnionType = 'foo' | 'bas'; // should fail

But you can't do that. As a workaround, you can make a new type function called Extends<T, U> which evaluates to U but only compile if U extends T, like this:

type Extends<T, U extends T> = U;

Then you can rewrite the invalid code to the following valid code:

type MySubUnion = Extends<MyUnionType, 'foo' | 'bar'>; // okay, compiles

and this:

type MySubUnion = Extends<MyUnionType, 'foo' | 'bas'>; // error:
// Type '"bas"' is not assignable to type 'MyUnionType'.

Does that help? Good luck!

like image 120
jcalz Avatar answered Oct 16 '22 14:10

jcalz


You could always flip the declaration order, though it's a bit odd with the particular names here.

type MySubUnion = 'foo' | 'bar';
type MyUnionType = MySubUnion | 'baz';

It's more in the lines of composing union types.

like image 2
What Would Be Cool Avatar answered Oct 16 '22 15:10

What Would Be Cool


Use the Exclude<Type, Union> utility type.

type MyUnionType = 'foo' | 'bar' | 'baz'

type SubType = Exclude<MyUnionType, 'baz'>

// SubType is now 'foo' | 'bar'
like image 1
Thomas Deutsch Avatar answered Oct 16 '22 14:10

Thomas Deutsch