简体   繁体   中英

TypeScript utility type over a discriminated union type

Given a discriminated union type like this:

type HomeRoute   = { name: 'Home' };
type PageRoute   = { name: 'Page'; id: number };
type SearchRoute = { name: 'Search'; text: string; limit?: number };

type Route = HomeRoute | PageRoute | SearchRoute;

I would like an utility type that takes the union type and its the discriminant (here the type of the name member: "Home" | "Page" | "Search" ) and returns the matching case:

type Discriminate<TUnion, TDiscriminant> = ???

type TestHome = Discriminate<Route, 'Home'>; // Expecting "HomeRoute" (structure)
type TestPage = Discriminate<Route, 'Page'>; // Expecting "PageRoute" (structure)

You can use the Extract predefined conditional type:

type HomeRoute   = { name: 'Home' };
type PageRoute   = { name: 'Page'; id: number };
type SearchRoute = { name: 'Search'; text: string; limit?: number };

type Route = HomeRoute | PageRoute | SearchRoute;

type TestHome = Extract<Route, { name: 'Home' }>; 
type TestPage = Extract<Route, { name: 'Page' }>; 

You can also create a generic version of Discriminate but not sure if it is worth it as you would need the field as well:


type Discriminate<TUnion, TField extends PropertyKey, TDiscriminant> = Extract<TUnion, Record<TField, TDiscriminant>>

type TestHome = Discriminate<Route, 'name', 'Home'>; // Expecting "HomeRoute" (structure)
type TestPage = Discriminate<Route, 'name', 'Page'>; // Expecting "PageRoute" (structure)

Playground Link

The most verbose solution would be:

type Discriminate<TUnion, TDiscriminant> = 
  TUnion extends {name: TDiscriminant} ? TUnion : never

type HomeMember = Discriminate<Route, 'Home'>;
type PageMember = Discriminate<Route, 'Page'>; 

We can use also existing utility type Extract which will do conditional type for use:

type Discriminate<TUnion, TDiscriminant> = Extract<TUnion, {name: TDiscriminant}>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM