Home  >  Q&A  >  body text

Make React props optional and required in TypeScript based on other props

I have a React button container component that returns a regular button, an icon button, or an icon-only button based on the type provided by the prop used by the user. The following are the related type definitions of the ButtonContainer component and Props:

type ButtonWrapperProp = {
    label?:string;
    icon?:React.ReactNode;
    onClick:()=>void;
    type:'text'|'icon'|'iconOnly';

}

export const ButtonContainer = (props: ButtonWrapperProps) => {
    const {
        type = 'text',
        onClick,
        icon = <></>,
        label = '',
    } = props;

    const rendorButton = () => {
        switch (type) {
            case 'text':
                return (
                    <Button onClick={onClick}>{label}  <Button/>
                );

            case 'iconOnly':
                return (
                     <IconButton onClick={onClick}>
                       {icon}
                     </IconButton>
                );

            case 'icon':
                return (
                     <Button startIcon={icon}>
                       {label}
                     </Button>
                );

            default:
                return (
                    <Button onClick={onClick}>{label}  <Button/>
                );
        }
    };
    return <>{rendorButton()}</>;
};

Here's how I use the ButtonContainer component:

<ButtonContainer type="iconOnly" onClick={onClick} icon={<DeleteIcon />}/>
<ButtonContainer type="text" onClick={onClick} label='Text button'/>
<ButtonContainer type="icon" onClick={onClick} label='Text and icon button' icon={<DeleteIcon />}/>

In the above code, the icon prop is optional because it is only used for icon buttons with icons and regular buttons. I want the icon prop to be optional only if the type passed from ButtonContainer is text and it should be a required prop if the type passed to ButtonContainer is icon or iconOnly.

P粉738046172P粉738046172398 days ago640

reply all(1)I'll reply

  • P粉803444331

    P粉8034443312023-09-18 12:22:20

    I would choose to use Distinguish the union type.

    The type attribute acts as a discriminator, allowing you to narrow down ButtonWrapperProps to union members.

    Also note that I can make each union member's properties required, which gives a good experience when using them - TS is smart enough to prompt which additional properties are required after the type is set .

    type TextButtonProps = {
      type:'text'
      label:string;
      onClick:()=>void;
    }
    
    type IconOnlyButtonProps = {
      type:'iconOnly'
      icon:React.ReactNode;
      onClick:()=>void;
    }
    
    type IconButtonProps = {
      type:'icon'
      icon:React.ReactNode;
      label:string;
      onClick:()=>void;
    }
    
    type ButtonWrapperProps = TextButtonProps | IconOnlyButtonProps | IconButtonProps;
    
    export const ButtonContainer = (props: ButtonWrapperProps) => {
      const renderButton = () => {
          switch (props.type) {
              case 'text':
                  return (
                      <Button onClick={props.onClick}>{props.label}  </Button>
                  );
    
              case 'iconOnly':
                  return (
                       <IconButton onClick={props.onClick}>
                         {props.icon}
                       </IconButton>
                  );
    
              case 'icon':
                  return (
                       <Button startIcon={props.icon}>
                         {props.label}
                       </Button>
                  );
          }
      };
      return <>{renderButton()}</>;
    };
    

    reply
    0
  • Cancelreply