1

Currently this function works fine

function wrapElement(elem: JSX.Element) {
  return ({ ...props }) => React.cloneElement(elem, { ...props })
}

I use it like this, because this way I can get intelliSense for tailwind classes

const Btn = wrapElement(<button className="[A LOT OF TAILWIND UTILITY CLASSES]" />)

But I'm trying to get it to return same type as it receives, so I could get intelliSense for attributes on intrinsic HTML elements. Right now inferred type is

function wrapElement(elem: JSX.Element): ({ ...props }: {
    [x: string]: any;
}) => React.FunctionComponentElement<any>.FunctionComponentElement<any> 

I tried some stuff and it all failed with all kinds of errors, at this point it feels like this could be to hacky, but maybe I don't understand something?

Alex l.
  • 213
  • 1
  • 14

1 Answers1

4

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>.

Linda Paiste
  • 38,446
  • 6
  • 64
  • 102