1

I have a component that normally requires a close method prop, so the interface would be as seen below:

interface Props {
  onClose: () => void;
  footer?: React.ReactElement
}

However when the footer prop is added the onClose should no longer be required. Currently I have to do the following to work around this.

<Modal
  footer={renderFooterWhichHasItsOwnCloseMethod()}
  onClose={null}
  >
    {children}
</Modal>

Is there a way to handle this with Typescript.

What I have done:

I googled and looked at some examples of but was not able to make it work. Below is one out of two results I found.

https://www.benmvp.com/blog/conditional-react-props-typescript/

Aaron
  • 2,364
  • 2
  • 31
  • 56
  • 2
    Does this answer your question? [typescript interface require one of two properties to exist](https://stackoverflow.com/questions/40510611/typescript-interface-require-one-of-two-properties-to-exist) – bugs Aug 23 '21 at 15:46

2 Answers2

3

If the intent is to have one or the other, you would be better off using a discriminating union type:

type Props = 
   { onClose: () => void } |
   { footer: React.ReactElement };

This will enforce using one or the other. But not both at the same time.

<Modal footer={renderFooterWhichHasItsOwnCloseMethod()}>
   {children}
</Modal>
// ok
<Modal onClose={() => console.log('Close!')}>
   {children}
</Modal>
// ok
<Modal onClose={() => console.log('Close!')}
       footer={renderFooterWhichHasItsOwnCloseMethod()}>
   {children}
</Modal>
// not ok
Patiu Dan
  • 501
  • 1
  • 5
  • 7
  • Is there a way to merge that type def with an interface. I know my example was simple but the component is actually complex. It has two interfaces being extended. – Aaron Aug 23 '21 at 16:00
  • Suppose you have a CommonProps interface containing properties that are common between the 2 use cases (with footer or onClose). `type Props = CommonProps & ({onClose: ...} | {footer: ...}` will create a type that will act like CommonProps + {onClose or footer} but not a combination of both. – Patiu Dan Aug 23 '21 at 16:06
  • I really appreciate the time and the answer. Thank you. – Aaron Aug 23 '21 at 16:18
1

You can accomplish this with type unions and aliases:

type Props = {
  onClose: () => void;
  footer?: React.ReactElement
} | {
  onClose?: () => void;
  footer: React.ReactElement
}

This way, at least onClose or footer are required, but never both.

Connor Low
  • 5,900
  • 3
  • 31
  • 52