[Question] Doing a component map
neo opened this issue · comments
I'm trying to do a classic component map by a type id like this:
const map = {
"one": ComponentOne,
"two": ComponentTwo,
}
and then use it by type id and hopefully it would be able to also infer the type of the prop for the corresponding component; how should I do it?
add as const
to the end of your object - I'm not sure if this is something we want in the cheatsheet though, it's very much plain 'ol typescript stuff
I think your looking for something like this:
import React from "react"
function One({oneProp}: {oneProp: number}) {
return <div>{oneProp}</div>
}
function Two({twoProp}: {twoProp: string}) {
return <div>{twoProp}</div>
}
const map = {
one: One,
two: Two,
}
function Switch<T extends keyof typeof map>({type, props}: {type: T, props: React.ComponentProps<typeof map[T]>}) {
const Component = map[type]
return <Component {...props} />
}
function Page() {
return <>
<Switch type="one" props={{oneProp: 2}} />
<Switch type="two" props={{twoProp: "hello"}} />
</>
}
However, <Component {...props} />
will have a TypeScript error here, because TypeScript doesn't really understand the relationship we provided in map
. Since we know this is safe, we could solve it with <Component {...props as any} />
. But then again, anything can be "solved" with any
.
Another way is to use a union instead of generic:
function Switch({type, props}: {type: "one", props: React.ComponentProps<typeof One>} | {type: "two", props: React.ComponentProps<typeof Two>}) {
const Component = map[type]
return <Component {...props as any} />
}
But we still need as any
there.
A solution without any
is to use switch
instead:
import React from "react"
interface OneProps {
oneProp: number
}
function One({oneProp}: OneProps) {
return <div>{oneProp}</div>
}
interface TwoProps {
twoProp: string
}
function Two({twoProp}: TwoProps) {
return <div>{twoProp}</div>
}
function Switch({type, props}: {type: "one", props: OneProps} | {type: "two", props: TwoProps}) {
switch (type) {
case "one": return <One {...props} />
case "two": return <Two {...props} />
}
}
function Page() {
return <>
<Switch type="one" props={{oneProp: 2}} />
<Switch type="two" props={{twoProp: "hello"}} />
</>
}
A bit less magic, but might be the best solution.
Fun problem to think about, but I agree that this is not really related to React, but rather a TypeScript challenge, so I don't think there's a need to add it to the cheatsheet.
Thank you @filiptammergard for all the details and options provided!! ❤️ (also thank you for understanding my use case exactly!)
I actually had union + switch/case at the end too, but was just wondering if TS can handle such a relatively common pattern – I guess not yet 😂
Not sure if this is worth noting in the cheatsheet, but at least now it's in an issue 😆
Thank you @orta too for the tips!