Home > Blockchain >  How to create a Typescript type guard for my union type
How to create a Typescript type guard for my union type

Time:01-23

I have the following Typescript types:

export type TeaserOne = { __typename: 'TeaserOne' } & TeaserOneItem;
export type TeaserTwo = { __typename: 'TeaserTwo' } & TeaserTwoItem;
export type TeaserThree = { __typename: 'TeaserThree' } & TeaserThreeItem;

export type Teaser = TeaserOne | TeaserTwo | TeaserThree;

export type FilteredTeasers = Exclude<Teaser, TeaserThree >;

export type TeaserItem = TeaserOneItem | TeaserTwoItem | TeaserThreeItem;

For example the item types looks a bit like:
// TeaserOneItem:

export interface TeaserOneItem {
  type: string;
  id: string;
  title: string;
  lead?: string;
}

// TeaserTwoItem:

export interface TeaserTwoItem {
  type: string;
  id: string;
  title: string;
  duration?: string;
  image?: Image;
}

In a certain React component I need to create a Typescript type guard:

const filteredItems = data.items?.filter(
    (item) =>
      item?.__typename.includes('TeaserOne') || item?.__typename.includes('TeaserTwo')
  );
// -->  Maybe also I can refactor above with a type guard, but how exactly?

{filteredItems.map((node, index) => {
          if (isTeaserOne() // <-- Here I want to use a type guard) {
            const teaserOne = node as TeaserOneType;

            return (
              <TeaserOne
                key={teaserOne.id}
                title={teaserOne.title}
                lead={teaserOne.lead}
              />
            );
          }
          const teaserTwo = node as TeaserTwoType;
          return (
            <TeaserTwo
              key={teaserTwo.id}
              title={teaserTwo.title}
              image={teaserTwo.image}
            />
          );
        })}

How do I create a nice and clean type guard function isTeaserOne()? So far I have:

const isTeaserOne = (teaser: TeaserItem): teaser is TeaserOneItem =>
    typeof teaser === 'object' && teaser.type.includes('TeaserOne');

But how do I have to use this? Or do I have to refactor above type guard function?

CodePudding user response:

In your code isTeaserOne = (teaser: Teaser) use Teaser class, so have the __typename property, you can use it directly

    const isTeaserOne = (teaser: Teaser): teaser is TeaserOne => teaser.__typename === 'TeaserOne';
    const isTeaserTwo = (teaser: Teaser): teaser is TeaserTwo => teaser.__typename === 'TeaserTwo';
    const isTeaserThree = (teaser: Teaser): teaser is TeaserThree => teaser.__typename === 'TeaserThree';
      
   

    filteredItems.map((node) => {
           // here typeof node is Teaser 
           if (isTeaserOne(node)) { 
                // here typeof node is TeaserOne
                <TeaserOne />
            }
            if (isTeaserTwo(node)) {
                // here typeof node is TeaserTwo
                <TeaserTwo />
                }
            if (isTeaserThree(node)) { 
               // here typeof node is TeaserThree
                <TeaserThree />
                }
            return "nothing";
        })

In fact, is suppose your code is incomplete, beacause in facts you do not need a typeguard here. a simple

if (node.__typename === "TeaserOne") { 
        // here typeof node is TeaserOne
        <TeaserOne />
    }

Would give the same results.

  • Related