Как передать в React.forwardRef дженерик в виде названия элемента?

Введение:

Необходимо написать компонент Button с использованием React.forwardRef, но с возможностью выбора отображаемого элемента, как это реализуется в некоторых библиотеках. Например, по умолчанию отображается элемент button, но если я передаю параметр href, то должна отображаться ссылка. Важный момент заключается в том, что мне нужно связать это поведение с ref - по умолчанию ref должен принимать ссылку типа React.Ref<HTMLButtonElement>, но если я передаю через props параметр href, то ref должен принимать ссылку типа React.Ref<HTMLAnchorElement>

Попытки:

Пока не сильно разбираюсь в тонкостях языка, но вот что у меня получилось накидать.

// общие параметры
type CommonProps = {
  children: React.ReactNode
  variant?: 'plain' | 'tonal' | 'filled' | 'outlined'
  justify?: 'start' | 'center' | 'end'
}

type Props<T extends 'a' | 'button' | never> = (
  T extends 'button' | 'a'
    ? T extends 'a'
      ? CommonProps & { href: string } & React.ComponentPropsWithoutRef<T>
      : CommonProps & { href?: never } & React.ComponentPropsWithoutRef<T>
    : CommonProps & { href?: never } & React.ComponentPropsWithoutRef<'button'>
)

const Button = React.forwardRef(<T extends 'a' | 'button' | never>(props: Props<T>, ref: React.Ref<React.ComponentRef<T>>) => {

  if (typeof props.href === 'string') {
    return (
      <a className="button" ref={ref as React.Ref<React.ComponentRef<'a'>>}>
        {props.children}
      </a>
    )
  }

  return (
    <button className="button" ref={ref as React.Ref<React.ComponentRef<'button'>>}>
      {props.children}
    </button>
  )

})

export default Button

Текущий результат:

На данный момент я чувствую что иду в нужном направлении, но все никак не могу понять что конкретно делаю не так, уже мозг вскипает, если честно. Повторюсь что конкретно я ожидаю:

  1. Вызов <Button /> должен ожидать атрибуты только для кнопки (ButtonProps);
  2. Вызов <Button href="/" /> должен ожидать атрибуты только для ссылки (AnchorProps);
  3. Вызов <Button ref={buttonRef} /> должен ожидать атрибуты только для кнопки (ButtonProps);
  4. Вызов <Button ref={anchorRef} /> должен ожидать атрибуты только для ссылки (AnchorProps).

Надеюсь понятно объяснил, заранее благодарю)


Ответы (0 шт):