Barris
Barris

Reputation: 1019

Typescript extract and create Union as a subset of a Union

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...

Upvotes: 38

Views: 14417

Answers (6)

Mark Swardstrom
Mark Swardstrom

Reputation: 18080

You can also use Extract<Type, Union> utility type.

Extract is the opposite of Exclude and more intuitive if you have a large union and just need a few of the items.

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

type MySubUnionType = Extract<MyUnionType, 'foo' | 'bar'>;

Upvotes: 1

Alexander
Alexander

Reputation: 139

Dynamic solution would be.

type SubTypedUnion<T> = T extends infer U ? U : never;

Upvotes: 0

Thomas Deutsch
Thomas Deutsch

Reputation: 2544

Use the Exclude<Type, Union> utility type.

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

type SubType = Exclude<MyUnionType, 'baz'>

// SubType is now 'foo' | 'bar'

Upvotes: 18

Yaron Landau
Yaron Landau

Reputation: 87

As mentioned in this post:

Possible to extend types in Typescript?

You can just write like that:

type MySubUnion = MyUnionType & ('foo' | 'bar');

Upvotes: 2

What Would Be Cool
What Would Be Cool

Reputation: 6758

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.

Upvotes: 4

jcalz
jcalz

Reputation: 327964

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!

Upvotes: 66

Related Questions