Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Function overloading with typescript on a react component?

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?

like image 753
user10741122 Avatar asked Oct 15 '25 03:10

user10741122


1 Answers

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
like image 181
captain-yossarian Avatar answered Oct 17 '25 16:10

captain-yossarian