Take a simple react component
interface MyProps {
color: string
name?: string
height?: number
isBoy?: boolean
// only add the following if isBoy is true
actionHero: string
nickname: string
}
function MyComponent(props: MyProps){
...
}
As you can see, the goal is that actionHero
and nickName
are required if isBoy
is set to true. Otherwise, they aren't used.
I assume this is done via function overloading in typescript but how do you do it in react?
You don't need to overload your component in this case. COnsider this example:
import React from 'react'
type Boy = {
color: string
name?: string
height?: number
isBoy: false
}
type ExtendedBoy = Omit<Boy, 'isBoy'> & {
actionHero: string
nickname: string
isBoy: true;
}
type Props = Boy | ExtendedBoy;
function MyComponent(props: Props) {
if (props.isBoy) {
props.nickname // stirng
} else {
props.isBoy // false
}
return <div></div>
}
const withBoy = <MyComponent isBoy color="red" actionHero={'batman'} nickname={'qwert'} /> // ok
const withoutBoy = <MyComponent isBoy={false} color="red" /> // ok
Playground
I have used discriminated unions instead function overloading.
BUT, nobody can't stop you if you still want to overload your component:
import React, { FC } from 'react'
type Boy = {
color: string
name?: string
height?: number
isBoy: false
}
type ExtendedBoy = Omit<Boy, 'isBoy'> & {
actionHero: string
nickname: string
isBoy: true;
}
const MyComponent: FC<Boy> & FC<ExtendedBoy> = (props) => {
if (props.isBoy) {
props.nickname // stirng
} else {
props.isBoy // false
}
return <div></div>
}
const withBoy = <MyComponent isBoy color="red" actionHero={'batman'} nickname={'qwert'} /> // ok
const withoutBoy = <MyComponent isBoy={false} color="red" /> // ok
Playground
Please be aware that intersection of functions produces function overloading FC<Boy> & FC<ExtendedBoy>
If you are interested in typescript validation in react components, see my article and my blog here and here
@NicoRichter asked a very good question: Why
React.createElement(MyComponent, { isBoy: false, color: 'red' });
is failing?
Before answering on this question, just try to change this overloading FC<Boy> & FC<ExtendedBoy>
to this one FC<ExtendedBoy> & FC<Boy>
. It might sound strange, but it is a "game changer", because we are dealing with overloadings. When createElements
infers a props from overloaded function it just grabs last overloading (by design). This is why it does not work in a way we expect.
Example:
function foo(a: string): number
function foo(a: number): string
function foo(a: number | string) {
return null as any
}
const fn = <Fn extends (...args: any[]) => any>(fn: Fn,...props:Parameters<Fn>) => {
}
const result = fn(foo,2) // ok, expects number
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With