'React + TS: Using generic types to get HTML props based on tag name

I have a common use case where I want a component that is simply a styled HTML tag. For example, the following works fine for creating style divs:

// Definitions
const styledDiv = (className: string) => {
    const StyledDiv = React.FC<HTMLAttributes<HTMLDivElement>> => props => {
        return <div {...props} className={classNames(className, props.className)} />;
    }

    return StyledDiv;
}

// Usage
const Foo = styledDiv(styles.foo);

const Bar = () => {
    return <Foo className={styles.bar} onClick={...} ... />
}

I am attempting to make this generic so that it will work for any HTML element, not just divs. I have tried the following:

// Definition
export const styledTag = <T extends keyof JSX.IntrinsicElements>(className: string, tag: T) => {
    const Tag = tag;

    const StyledTag: React.FC<HTMLAttributes<T>> = props => {
        const classes = classNames(className, props.className);
        return <Tag {...props} className={classes}/>;  
    };

    return StyledTag;
};

// Desired usage:
const Foo = styledTag(styles.foo, 'div');

const Bar = styledTag(styles.bar, 'span');

const Baz = () => {
    return (
        <div>
            <Foo ... />
            <Bar ... />
        </div>
    );
}

Unfortunately, I am getting a Type X is not assignable to type Y error when I try to spread my props on the tag (<Tag {...props} />). Is there a way to accomplish this generically, or will I have to repeat this code for every HTML tag I want?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source