Dynamic tag typing in React with Typescript

Asked

Viewed 268 times

0

What’s going on

I have a button component with its dynamic html tag [a, button], but the way I called it shows error in some props like the onClick, anchor who receives an event of type click on anchor and on the button an event of the type click on button.

I wanted to know a way to define what exactly the button receives from props and not concatenate as I did there and occur these errors.

Typing that seems to be wrong

On the operator & I’ve tried to trade for |, but there are other mistakes.

FC<Props & ( 
    AnchorHTMLAttributes<HTMLAnchorElement>
    & ButtonHTMLAttributes<HTMLButtonElement> 
)>

The component

import React, {
  AnchorHTMLAttributes,
  ButtonHTMLAttributes,
  FC,
  HTMLProps,
  ReactElement
} from 'react';

const htmlTags = {
  a: 'a',
  button: 'button',
} as const;

type HTMLTag = keyof typeof htmlTags;

interface Props {
  tag: HTMLTag;
}

const tags = {
  a: (props: AnchorHTMLAttributes<HTMLAnchorElement>): ReactElement => <a {...props} />,
  button: (props: ButtonHTMLAttributes<HTMLButtonElement>): ReactElement => <button {...props} />,
};

const Button: FC<Props & (
  & AnchorHTMLAttributes<HTMLAnchorElement>
  & ButtonHTMLAttributes<HTMLButtonElement>
)> = ({
  children,
  tag = 'button',
  ...props
}) => {
  const Tag = tags[tag];
  const isAnchorTag = tag === 'a';
  const isButtonTag = tag === 'button';
  const restProps = { ...props };

  if (isAnchorTag) {
    delete restProps.href;
  }

  if (isButtonTag) {
    restProps.type = restProps.type ?? 'button';
  } else {
    delete restProps.type;
  }

  return (
    <Tag {...restProps}>
      {children}
    </Tag>
  );
};

The call of the component

function handleClickToCopyBarcode(): void {
  // ...
}

<Button onClick={handleClickToCopyBarcode} className={classes} {...props}>
  teste
</Button>

Error

Type '((event: MouseEvent<HTMLButtonElement, MouseEvent>) => void) | (() => void)' is not assignable to type '((event: MouseEvent<HTMLAnchorElement, MouseEvent>) => void) & ((event: MouseEvent<HTMLButtonElement, MouseEvent>) => void)'.

  • Edit your question to include the type that function handleClickToCopyBarcode assumes. Implementation is not so important in this case, but may also include...

  • I edited the question and put how I declared the function handleClickToCopyBarcode.

  • putting a any works? replace the 2 with a any, or create a manual interface that you mix the 2, I believe you can use the spreed Operator to create this mixed interface

  • @Joannis, the problem is, when using the any, loses one of the main benefits that Typescript brings, that is, security.

  • I would like this to be solved in the button component, because this problem can be repeated in several other attributes: onFocus, onBlur, onHover... In addition to requiring that each one who uses it needs to think about how to solve it.

No answers

Browser other questions tagged

You are not signed in. Login or sign up in order to post.