I have a group of consistent types and two groups of conditional props like so:
export interface Props {
label: string
children?: ReactNode | undefined
}
export type OpacityConditionalProp =
| {
hasOpacitySlider: true
opacitySliderState: number | number[]
opacitySliderSetter: Dispatch<SetStateAction<number | number[]>>
}
| {
hasOpacitySlider?: false | undefined
opacitySliderState?: undefined
opacitySliderSetter?: undefined
}
export type VisibilityConditionalProp =
| {
hasVisibilityToggle: true
isVisible: boolean
onChangeVisibility: (v: boolean) => void
}
| {
hasVisibilityToggle?: false | undefined
isVisible?: undefined
onChangeVisibility?: undefined
}
And in the component I declare it like:
const LayerItem: FC<Props & OpacityConditionalProp & VisibilityConditionalProp> = ({
label,
hasOpacitySlider,
opacitySliderState,
opacitySliderSetter,
isVisible,
onChangeVisibility,
hasVisibilityToggle,
children,
})
It works well when I use it like this:
<LayerItem
hasOpacitySlider={false}
hasVisibilityToggle={false}
label="Range statistics"
>
or like this
<LayerItem
isVisible={show}
label="Contours"
opacitySliderSetter={setOpacity}
opacitySliderState={opacity}
hasOpacitySlider
hasVisibilityToggle
onChangeVisibility={setShow}
>
or like this
<LayerItem
hasOpacitySlider={false}
isVisible={showBathy}
label="Bathymetry contours"
hasVisibilityToggle
onChangeVisibility={setShowBathy}
>
However, it throws an error when I use it like this:
const hasOrthoData = Boolean(orthoData)
<LayerItem
hasOpacitySlider={hasOrthoData}
hasVisibilityToggle={hasOrthoData}
isVisible={showOrthoLayer}
opacitySliderSetter={setOrthoLayerOpacity}
opacitySliderState={orthoLayerOpacity}
onChangeVisibility={setShowOrthoLayer}
label="Orthophoto"
>
I expect that if I pass true to hasVisibilityToggle
or to hasOpacitySlider
, it will figure out when to use which type and what to render. However, it doesn't work well when I pass ternary operators to the props of the component, complaining with that error.
Maybe there is a better and clean way to achieve this?
I managed to get this working, but I am not pleased with the solution:
<LayerItem
{...(hasOrthoData
? ({
hasOpacitySlider: true,
hasVisibilityToggle: true,
isVisible: showOrthoLayer,
opacitySliderSetter: setOrthoLayerOpacity,
opacitySliderState: orthoLayerOpacity,
onChangeVisibility: setShowOrthoLayer,
label: 'Orthophoto',
} as Props & OpacityConditionalProp & VisibilityConditionalProp)
: ({
label: 'Orthophoto',
} as Props))}
>
You are on the right path AFAIK. TypeScript compiler isn't smart enough to detect at compile time the different combination of properties you are sending to the
LayerItem
component.You could make it look cleaner anyways
label
prop will go anyways independent of the OpacityConditionalProps.