It is basically impossible to get the correct props from a JSX.Element
. You can achieve the design that you want, but you should pass in the element name and the props as separate arguments rather than passing in a JSX.Element
.
This code can accept an element name like 'button'
or any React component. It returns a function component with the same props. I am not dropping any props from the returned component because it seems like you are using this for setting defaults rather than dropping requirements.
import React, { ComponentType, ComponentProps } from "react";
const wrapElement = <
C extends keyof JSX.IntrinsicElements | ComponentType<any>
>(
Component: C,
presetProps: Partial<ComponentProps<C>>
) => (props: ComponentProps<C>) => {
const merged: ComponentProps<C> = { ...presetProps, ...props };
return <Component {...merged} />;
};
const Btn = wrapElement("button", {
className: "[A LOT OF TAILWIND UTILITY CLASSES]"
});
const Dbl = wrapElement(Btn, { onClick: () => alert("clicked") });
const Test = () => {
return <Dbl>Click</Dbl>;
};
Typescript Playground Link
You might want to customize your merge behavior to combine className
or style
properties rather than overriding them.
Note: when I tried to merge the props inline like <Component {...presetProps} {...props} />
I got a weird error "Type Partial<ComponentProps<C>> & ComponentProps<C>
is not assignable to type IntrinsicAttributes & LibraryManagedAttributes<C, any>
." So that is why I am merging the props on a separate line and annotating the type as ComponentProps<C>
instead of the inferred type Partial<ComponentProps<C>> & ComponentProps<C>
.