user29347780
user29347780

Reputation: 1

Error when setting a default 'as' prop in a polymorphic React component

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:

What happens:

What I've Tried

Upvotes: 0

Views: 23

Answers (0)

Related Questions