#Union type

44 messages · Page 1 of 1 (latest)

lyric shale
#

Hey guys, I want to create a react component with an union type as props :

type NavigationForTestsProps = {
  activeLabel: string;
  campaignListType: 'test';
  activeTestsCount: number;
  exclusionGroupLabel: string;
  exclusionGroupTestCount: number;
  activeTab: NavigationTab;
  setActiveTab: (activeTab: NavigationTab) => void;
};

type NavigationForPersonalizationProps = {
  activeLabel: string;
  campaignListType: 'personalization';
  activeTestsCount: number;
  exclusionGroupLabel: undefined;
  exclusionGroupTestCount: undefined;
  activeTab: NavigationTab;
  setActiveTab: (activeTab: Exclude<NavigationTab, 'exclusion-group'>) => void;
};

export const Navigation: FC<NavigationForTestsProps | NavigationForPersonalizationProps> = ({
  activeLabel,
  activeTestsCount,
  exclusionGroupLabel,
  exclusionGroupTestCount,
  activeTab,
  setActiveTab,
  campaignListType,
}) => {

I want that if campaignListType is 'test', exclusionGroupLabelet exclusionGroupTestCountare defined, for now it works but with

  exclusionGroupLabel: undefined;
  exclusionGroupTestCount: undefined;

is there another solution for my case?

charred fractal
#

yes, there are quite a few solutions for that

#

the easiest one would be to use discriminated unions

#

!hb Discriminated unions

pearl pecanBOT
charred fractal
#

basically, you can create a field whose only purpose is to tell what version of the props you are using

#

once you figure what version you got, you can then access the fields that are unique to that version

#

you might want to read more than just the section on discriminated unions, but the entier "Narrowing" article

#

@lyric shale

#

also, a few remarks

  • you can create a common type for thet props that will be used by both versions, so you don't have to repeat yourself
  • React.FC should be avoided, instead use the type of your props on the props object directly
lyric shale
#

In fact my solution doesn't work because when I use the component I have this error :

Type '"test"' is not assignable to type '"personalization"'.

I'm gonna read what you sent ;)

#

why should React.FC be avoided?

lyric shale
charred fractal
lyric shale
#

yes that's what I'm doing

...(campaignListType === 'test'
            ? [
                {
                  key: exclusionGroupLabel, // well typed
                  text: `${exclusionGroupLabel}`,
                  ...(!areCountersLoading && { totalElements: exclusionGroupTestCount }),
                  selected: activeTab === 'exclusion-group',
                  onClick: () => setActiveTab('exclusion-group'),
                },
              ]
            : []),
#

it's working but

pearl pecanBOT
#
ascor8522#0

Preview:```ts
import React from "react"

declare interface NavigationTab {}
interface CommonNavigationProps {
activeLabel: string
activeTestsCount: number
activeTab: NavigationTab
}
interface NavigationForTestsProps
extends CommonNavigationProps {
campaignListTyp
...```

#
Kalo0m#4644

Preview:```ts
import React from "react"

declare interface NavigationTab {}
interface CommonNavigationProps {
activeLabel: string
activeTestsCount: number
activeTab: NavigationTab
}
interface NavigationForTestsProps
extends CommonNavigationProps {
campaignListTyp
...```

lyric shale
#

how can I access to exclusionGroupLabel in the component ?

#

and why can't I use the component like in the playground?

#

@charred fractal

charred fractal
#

you cannot access exclusionGroupLabel in the parameters because it might not exist

#

so you need do access it later, after performing a check on campaignListType, using an if statement

lyric shale
#

so I have to keep the props variable instead of destructuring the object

lyric shale
charred fractal
#

what do you mean by "cannot use"?

#

you defined type as a union
TS doesn't know what version it is
it might be of type 'test', and have some props missing

#

you have to figure out what version it is first, then pass all of the props necessary next

#

remember you can always pass the props by spreading an object
and you can give that object the proper type

#

that is also an option

lyric shale
#

Do you know why can’t ts know what version it is?

#

Like ts could understand that there is the CampaignListType twice and create the union then?

#

Just like when we declare multiple times the signature of a function (I don’t remember the name of that pattern)

charred fractal
#

no, that's not how it works
you wrote something like "type is A or B" but the code you wrote only accept "A" or only accept "B", not "A or B"

#

so you have to figure out if you are passing "A" or if you are passing "B" first, then pass the info as parameters

pearl pecanBOT
#
ascor8522#0

Preview:```ts
import React from "react"

declare interface NavigationTab {}
interface CommonNavigationProps {
activeLabel: string
activeTestsCount: number
activeTab: NavigationTab
}
interface NavigationForTestsProps
extends CommonNavigationProps {
campaignListTyp
...```

lyric shale
pearl pecanBOT
#
Kalo0m#4644

Preview:```ts
import React from "react"

declare interface NavigationTab {}
interface CommonNavigationProps {
activeLabel: string
activeTestsCount: number
activeTab: NavigationTab
}
interface NavigationForTestsProps
extends CommonNavigationProps {
campaignListTyp
...```

lyric shale
#

and do you know why campaignListType's type is 'test' | 'personalization' ?

#

line 23

charred fractal
#

@lyric shale it's from the type of the props

#

NavigationForTestsProps says it should be "test"
NavigationForPersonalizationProps says it should be "personalization"

and since the props are either NavigationForTestsProps OR NavigationForPersonalizationProps => "test" OR "personalization"

lyric shale
#

and there is no way to get that dynamic inferrence? without the union and with something else?