import { ReactComponent as GreenIcon } from '../assets/green.svg'
import { ReactComponent as YellowIcon } from '../assets/yellow.svg'
import { ReactComponent as RedIcon } from '../assets/red.svg'
const GreenIconMemoized = memo(GreenIcon)
const YellowIconMemoized = memo(YellowIcon)
const RedIconMemoized = memo(RedIcon)
const STATUSES = ['ready', 'delay', 'stop']
const icons = new Map([
['ready', GreenIconMemoized],
['delay', YellowIconMemoized],
['stop', RedIconMemoized]
])
const [sampleStatus, setSampleStatu] = useObserver(() => [
sampleSTORE.sampleStatus,
sampleSTORE.setSampleStatus
])
const sampleArray: ComponentType[] = useMemo(() => {
return STATUSES.map((status: string) => {
const Icon = icons.get(status)
return {
id: status,
label: status,
isSelected: status === sampleStatus,
onSelect: (): void => {
setSampleStatus(status)
},
leftComponent: Icon ? <Icon /> : undefined
}
})
}, [sampleStatus])
In the code above, what is the best way to pass the Icon value as a react component without the need of a conditional render? Both STATUSES and icons are hard coded into the system, and therefore are not really variable - the else condition will never be reached. But if I attempt to pass for example:
...
leftComponent: <Icon />
...
I am given the error "JSX element type 'Icon' does not have any construct or call signatures."
CodePudding user response:
I would recommend using an object instead of a Map. That way, typescript can know exactly which keys are valid, and thus knows that undefined
is impossible (as long as you provide a valid key).
const STATUSES = ['ready', 'delay', 'stop'] as const;
const icons = {
ready: GreenIconMemoized,
delay: YellowIconMemoized,
stop: RedIconMemoized
}
//...
return STATUSES.map((status) => {
const Icon = icons[status];
// ...
leftComponent: <Icon />
// ...
Note that i used as const
on the STATUSES. This causes typescript to infer the type to be readonly ["ready", "delay", "stop"]
, not string[]
. That means that later, when you map over the array, status
is of type "ready" | "delay" | "stop"
, not string
, and therefore you've proven to typescript that you'll only access valid keys.
If you prefer, you could do the types more explicitly, as in:
const STATUSES: ("ready" | "delay" | "stop")[] = ['ready', 'delay', 'stop'];
// ...
return STATUSES.map((status: "ready" | "delay" | "stop") => {
// OR:
const STATUSES: (keyof typeof icons)[] = ['ready', 'delay', 'stop'];
// ...
return STATUSES.map((status: keyof typeof icons) => {