Hey
Hey

Reputation: 50

How to use either this type or another based on props only? React Typescript Discriminating Unions

I have a componenet:

type Base = {
    color: string
}

type Button = {
    to: string
} & Base

type Link = {
    link: string
    linkNewTab: boolean
} & Base

type ComponentProps = Button | Link 

export const Button: React.FC<ComponentProps> = (props) => {
    return (<>
        {props.link ? <Link newTab={props.linkNewTab}>...</Link> : props.to && <Button>...</Button>}
    </>)
}

Typescript Error:

Property 'link' does not exist on type 'PropsWithChildren<ComponentProps>'.
  Property 'link' does not exist on type '{ to: string; } & Base & { children?: ReactNode; }'.ts(2339)

What I don't want:

Information: 4.5.4 TypeScript Version

Upvotes: 0

Views: 1477

Answers (2)

jsejcksn
jsejcksn

Reputation: 33856

The problem is that you are attempting to access a value at property which might not exist. Instead, you can check to see if that property exists in the object (by using the in operator) before trying to access its value. This will also discriminate the union:

TS Playground

// instead of this:
if (props.link) {}
//        ^^^^
// Property 'link' does not exist on type 'Button | Link'.
//   Property 'link' does not exist on type 'Button'.(2339)

// prop in obj
if ('link' in props) {
  props.color // ok
  props.link // ok
  props.linkNewTab // ok
}
else {
  props.color // ok
  props.to // ok
}

Upvotes: 2

Nicholas Tower
Nicholas Tower

Reputation: 85152

You can check whether the link property exists by using the in operator. This will narrow down the type without accessing the property, and so typescript will allow it:

<>
  {"link" in props ? (
    <Link newTab={props.linkNewTab}>...</Link>
  ) : (
    "to" in props && <Button>...</Button>
  )}
</>

Upvotes: 1

Related Questions