Home > other >  How to select a key from a discriminated union type?
How to select a key from a discriminated union type?

Time:01-13

I have a disciminated union

type MyDUnion = { type: "anon"; name: string } | { type: "google"; idToken: string };

I want to access the name key from the discriminative union, from the MyDUnion type directly. Something like this

type Name = MyDUnion['name']

But typescript won't allow that

Property 'name' doesn't exist on type '{ type: "anon"; name: string } | { type: "google"; idToken: string }'

How can I access it?

To be clear, this is not a valid solution:

type MyName = string;
type MyDUnion = { type: "anon"; name: MyName } | { type: "google"; idToken: string };
type Name = MyName; // this line would be in a different file

This is not valid, because then I would have to export both MyName and MyDUnion types to be used elsewhere.

Any ideas?

CodePudding user response:

In order to filter union of objects, usually you need to use Extract: The easy way:

type Result = Extract<MyDUnion , {type: "anon"}>

The more robust:

type MyDUnion = { type: "anon"; name: string } | { type: "google"; idToken: string };

type Values<T> = T[keyof T]

type Filter<Union, Type extends Partial<Union>> = Extract<Union, Type>

type Result = Filter<MyDUnion, { type: 'anon' }>

CodePudding user response:

In order to get the required key, you have to somehow tell the Typescript compiler

Hey compiler, I want to know about this object, under a very specific case

In this case, you want to know about the object, when the type ==='anon'. So, taking your example,

type MyDUnion = { type: "anon"; name: string } | { type: "google"; idToken: string };

type SpclCase = MyDUnion & {type: "anon"}

By doing so, you're getting information about when theses two cases overlap. Now you can just index the name key directly.

type Name = SpclCase['name']

If you want to make this more robust, we can make sure that the narrowing type we use ({type: 'anon'} in this case), satisfies the required shape. Here's a little impromptu solution for that.

type DiscriminatorObj = { type: 'anon' }
type RequiredCase = DiscriminatorObj extends Pick<MyDUnion, "type"> ? DiscriminatorObj: never;
type SpclCase = (RequestBody & RequiredCase);
type Name = SpclCase['name']

I know its a little rough around the edges, but you can always extract this into generic functions and use them as you please. I merely showed you the fundamental logic. You can use it to make it more aesthetically pleasing.

  • Related