Reputation: 699
For a button link component, I'm trying to choose between HTMLButtonAttributes or HTMLAnchorAttributes based on passed props. My knowledge of TypeScript is quite limited.
I have the following types and interfaces:
interface CommonButtonProps {
// custom props
}
export interface ButtonProps extends CommonButtonProps,
React.ButtonHTMLAttributes<HTMLButtonElement> {}
export interface ButtonLinkProps extends CommonButtonProps,
React.AnchorHTMLAttributes<HTMLAnchorElement> {
href: string;
}
export type ButtonOrButtonLinkProps = ButtonProps | ButtonLinkProps;
export const isButtonLink = (props: ButtonOrButtonLinkProps,): props is ButtonLinkProps => guard logic
And the following Button component with ButtonOrButtonLinkProps:
export const Button: React.FC<ButtonOrButtonLinkProps> = props => {
const buttonLink = isButtonLink(props);
const commonProps = {
};
const linkProps: ButtonLinkProps = {
...commonProps,
onClick: (event: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
// Do stuff
if (props.onClick) {
props.onClick(event); // Error
}
},
};
const buttonProps: ButtonProps = {
...commonProps,
type: 'button',
onClick: (event: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
// Do other stuff
if (props.onClick) {
props.onClick(event); // Error
}
},
};
return (
<StyledBtn {...(buttonLink ? linkProps : buttonProps)}>
{children}
</StyledBtn>
);
};
The following line throws an error:
if (props.onClick) {
props.onClick(event); // Error
}
Argument of type 'MouseEvent<HTMLButtonElement, MouseEvent>' is not assignable to parameter of type 'MouseEvent<HTMLButtonElement, MouseEvent> & MouseEvent<HTMLAnchorElement, MouseEvent>'. Type 'MouseEvent<HTMLButtonElement, MouseEvent>' is not assignable to type 'MouseEvent<HTMLAnchorElement, MouseEvent>'. Type 'HTMLButtonElement' is missing the following properties from type 'HTMLAnchorElement': charset, coords, download, hreflang, and 19 more.
It seems the current type of onClick
is
Why is the event type and intersection of MouseEvent of HTMLButtonElement &
MouseEvent of HTMLAnchorElement ? I kinda expected it to be an union - either one or the other ?
Any idea how to fix the issue ?
Upvotes: 1
Views: 1147
Reputation: 704
Your component's props is public API of isolated code block, so you don't want to expand it too far. The best solution would be not to include all of link/button props into Button
props, but include only necessary ones
The problem is that typescript does not understand what onClick
type is, because it can be different based on props you pass. By the time typescript comes to props.onClick(execution)
there are still ambiguity in type of props.
Use your typeguard to let typescript know what type of props is
export const Button: React.FC<...> = props => {
// props is of type `ButtonProps | ButtonLinkProps` here
// so `onClick` is ambiguous too
if (isButtonLink(props)) {
// Do button link stuff
// props is of type `ButtonLinkProps` here
} else {
// Do button stuff
// props is of type `ButtonProps` here
}
// props is again of type `ButtonProps | ButtonLinkProps` here
return (...);
}
Upvotes: 1