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.
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 .