Home > OS >  Dynamic react component type with Link as type
Dynamic react component type with Link as type

Time:11-10

I am struggling with typing of the following dynamic button component which can handle either links or onClick events. This is is a simplified snippet of the component I now try to refactor to TypeScript.

import React from 'react'
import { Link } from 'react-router-dom'

type Props = {
  children: React.ReactNode,
} & (
  {
    to: string,
    type: undefined,
    onClick: undefined,
  } | {
    to: undefined,
    type: 'button' | 'submit',
    onClick: (event: React.MouseEvent<HTMLButtonElement>) => void,
  }
)

export default function Button(props: Props) {
  const { children, to } = props
  const TagName = to ? Link : 'button'
  return (
    <TagName {...props}>
      {children}
    </TagName>
  )
}

It gives me the following error:

Type '{ children: ReactNode; to: string; onClick: undefined; } | { children: ReactNode; to: undefined; onClick: (event: MouseEvent<HTMLButtonElement, MouseEvent>) => void; }' is not assignable to type 'IntrinsicAttributes'.
  Type '{ children: ReactNode; to: string; onClick: undefined; }' has no properties in common with type 'IntrinsicAttributes'.

Guess I have to define the type of TagName but I can't figure out the correct type. Any advice?

It's the Link component type that causes the problem as I can get it working without it.

CodePudding user response:

add ? for optional properties which handle the undefined values

type Props = {
  children: React.ReactNode,
  to?: string,
  onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void,

} 

CodePudding user response:

Typescript won't allow you to do that due to incompatibility between your component props vs button & link's props type declarations. So have a common field that can distinguish button props and Link props, which in this case use type property of Props.

import { Link } from 'react-router-dom'
type Props = {
  children: React.ReactNode
} & (
  | {
      to: string
      type: 'url'
    }
  | {
      type: 'submit' | 'button'
      onClick: (event: React.MouseEvent<HTMLButtonElement>) => void
    }
)

export default function Button(props: Props) {
  if (props.type === 'url') {
    return <Link {...props} />
  }
  return <button {...props} />
}

  • Related