Reputation: 1
I'm building a polymorphic Button component in React using TypeScript. The component should allow users to change the underlying HTML tag via an as prop, with a default of 'button'. However, I'm encountering TypeScript errors when trying to handle the default as prop.
Here is the error:
Type 'Omit<ButtonProps<TagName>, "as" | "size" | "variant"> & { as: "button" | TagName; }' is not assignable to type 'IntrinsicAttributes & { as?: "button" | TagName | undefined; } & Omit<HTMLAttributes["button" | TagName], "ref">'.
// Type 'Omit<ButtonProps<TagName>, "as" | "size" | "variant"> & { as: "button" | TagName; }' is not assignable to type 'Omit<HTMLAttributes["button" | TagName], "ref">'.
);
I guess there are libraries which can do this, and the usage of React.CreateElement
is often not recommended, but I also want to know how to do it for a better understanding of React and TypeScript. I am also looking for a better way to do it.
Note that I will use the polymorphic component (HTMLComponent
) for other things and not just buttons.
import React from "react";
type HTMLAttributes = {
button: React.DetailedHTMLProps<React.ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>,
a: React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>,
div: React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>,
form: React.DetailedHTMLProps<React.FormHTMLAttributes<HTMLFormElement>, HTMLFormElement>
}
type HTMLComponentProps<TagName extends keyof HTMLAttributes = 'div'> = {
as?: TagName
} & Omit<HTMLAttributes[TagName], 'ref'>
// Base component to build other polymorphic components from
const HTMLComponent = <TagName extends keyof HTMLAttributes = 'div'>({ as = 'div' as TagName, ...props }: HTMLComponentProps<TagName>) => {
return React.createElement(as, props);
}
// Button Component
type ButtonProps<TagName extends keyof HTMLAttributes = 'button'> = HTMLComponentProps<TagName> & {
variant?: 'primary' | 'secondary',
size?: 'sm' | 'md' | 'lg',
};
const Button = <TagName extends keyof HTMLAttributes = 'button'>(
props: ButtonProps<TagName>
) => {
const { size, variant, as='button', ...htmlComponentProps } = props;
return (
<HTMLComponent {...htmlComponentProps} as={as}/>
// error here : Type 'Omit<ButtonProps<TagName>, "as" | "size" | "variant"> & { as: "button" | TagName; }' is not assignable to type 'IntrinsicAttributes & { as?: "button" | TagName | undefined; } & Omit<HTMLAttributes["button" | TagName], "ref">'.
// Type 'Omit<ButtonProps<TagName>, "as" | "size" | "variant"> & { as: "button" | TagName; }' is not assignable to type 'Omit<HTMLAttributes["button" | TagName], "ref">'.
);
};
//Components usage example
const App = () => {
<HTMLComponent as="form" onClick={e => {}}/>
return <Button as="div" onClick={e => {}}/>
}
What I expect:
<button>
element when the as prop is not provided.What happens:
What I've Tried
<HTMLComponent>
, but it caused type conflicts.Omit
, Partial
) without success.Upvotes: 0
Views: 23