区分联合类型的TypeScript实用程序类型

给定这样的区分联合类型:

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)
评论
  • yvero
    yvero 回复

    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

  • 一筐猪
    一筐猪 回复

    最冗长的解决方案是:

    type Discriminate<TUnion, TDiscriminant> = 
      TUnion extends {name: TDiscriminant} ? TUnion : never
    
    type HomeMember = Discriminate<Route, 'Home'>;
    type PageMember = Discriminate<Route, 'Page'>; 
    

    我们还可以使用现有的实用程序类型摘录,它将提取条件类型以供使用:

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