'Overload React component

Considering we have a React component that depending on different props can render either <button> or <a> or React Router <Link>

Not sure if it is even possible in this case but how can we overload this component to accept the correct props for each case?

const Button: FC<ButtonProps> = forwardRef(
  (
    {
      className,
      disabled,
      onClick,
      children,
      href,
      to,
      downloadable,
      ...rest
    },
    ref
  ) => { ... }

In addition to some shared props, unique props combination in each case is

interface BaseProps {
  bSize?: ButtonSize;
  bType?: ButtonType;
  bLayout?: ButtonLayout;
}

interface ButtonBaseProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  onClick?: (event: MouseEvent<HTMLButtonElement>) => void;
  ref?: Ref<HTMLButtonElement>;
}

interface ExternalLinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
  onClick?: (event: MouseEvent<HTMLAnchorElement>) => void;
  ref?: Ref<HTMLAnchorElement>;
  href?: string;
  downloadable?: boolean;
}

interface ReactRouterLinkProps
  extends ForwardRefExoticComponent<LinkProps & RefAttributes<HTMLAnchorElement>> {
  onClick?: (event: MouseEvent<HTMLAnchorElement>) => void;
  ref?: Ref<HTMLAnchorElement>;
  to?: string;
}

export type ButtonProps = BaseProps & (ButtonBaseProps | ExternalLinkProps | ReactRouterLinkProps);

Normally for a function, I would do something like this

function foo(bar: string[]): string[];
function foo(bar: number[]): number[];
function foo(bar: string[] | number[]): string[] | number[] { ... }


Solution 1:[1]

What you did is correct, you need just one thing, you need to add an additional prop type to help TypeScript to know which type you are using in your code:

import * as React from 'react';
import {LinkProps} from 'react-router-dom';
import './style.css';

export default function App() {
  return (
    <CustomComponent type='anchor' bSize='big' />
  );
}

interface CommonProps {  
  bSize?: any;
  bType?: any;
  bLayout?: any;
}

interface ButtonProps extends React.HTMLProps<HTMLButtonElement>  {
  type: 'button'
}

interface  AnchorProps extends React.HTMLProps<HTMLAnchorElement>  {
  type: 'anchor'
}

interface CustomLinkProps extends LinkProps {
  type: 'link'
}

type Props = (ButtonProps | AnchorProps | CustomLinkProps) & CommonProps

function CustomComponent (props: Props) {
  return <div></div>
}

And don't forget to use Discriminating Unions, check this: https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html

Demo: https://stackblitz.com/edit/react-ts-3yfbmt?file=App.tsx

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1