Dado un tipo de unión discriminada como este:

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

type Route = HomeRoute | PageRoute | SearchRoute;

Me gustaría un tipo de utilidad que tome el tipo de unión y sea el discriminante (aquí el tipo del nombre del miembro: "Home" | "Page" | "Search") y devuelve el caso coincidente:

type Discriminate<TUnion, TDiscriminant> = ???

type TestHome = Discriminate<Route, 'Home'>; // Expecting "HomeRoute" (structure)
type TestPage = Discriminate<Route, 'Page'>; // Expecting "PageRoute" (structure)
1
Romain Deneau 7 feb. 2020 a las 13:39

2 respuestas

La mejor respuesta

La solución más detallada sería:

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

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

Podemos usar también el tipo de utilidad existente Extract, que hará un tipo condicional para su uso:

type Discriminate<TUnion, TDiscriminant> = Extract<TUnion, {name: TDiscriminant}>
1
Maciej Sikora 7 feb. 2020 a las 10:53

Puede usar el tipo condicional predefinido Extract:

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' }>; 

También puede crear una versión genérica de Discriminate pero no está seguro de si vale la pena, ya que también necesitaría el campo:


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)

Enlace de juegos

2
Titian Cernicova-Dragomir 7 feb. 2020 a las 10:44