react-polymorphic-types icon indicating copy to clipboard operation
react-polymorphic-types copied to clipboard

Typing required props for `as`

Open rafgraph opened this issue 4 years ago • 5 comments

Hi, is it possible to type the props that as is required to accept? Without this it is easy to pass in an as prop that is not compatible with the polymorphic component. Thanks.

Using the heading example in the readme (also see this codesandbox for a live reproduction):

// Heading from readme:
function Heading<
  T extends React.ElementType = typeof HeadingDefaultElement
>({ as, color, style, ...restProps }: HeadingProps<T>) {
  const Element: React.ElementType = as || HeadingDefaultElement;
  return <Element style={{ color, ...style }} {...restProps} />;
}

const MyHeading: React.FC<{}> = ({ children }) => <h1>{children}</h1>;

function App() {
  return (
    <>
      <Heading as={MyHeading} color="green">
        This should result in a type error because MyHeading doesn't accept a style prop,
        but typescript thinks it's fine even though it doesn't work.
      </Heading>
      {/* this errors because MyHeading doesn't accept a style prop
        <MyHeading style={{ color: "green" }}>MyHeading</MyHeading>
      */}
    </>
  );
}

rafgraph avatar May 12 '21 17:05 rafgraph

You might have some luck with Typescript's conditional types, but I'm not entirely sure if that'll work.

You could also try adding a value for the generic parameter (i.e. props type) of React.ElementType in T extends React.ElementType. For example: Heading<P extends { style: any }, T extends React.ElementType<P> = typeof HeadingDefaultElement>.

These are just ideas however, so you'd have to do some experimenting on your own.

Mesoptier avatar May 12 '21 17:05 Mesoptier

Thanks @Mesoptier, I tried your suggestions but wasn't able to get it working with react-polymorphic-types. Here's a codesandbox if you want to try.

rafgraph avatar May 13 '21 18:05 rafgraph

@rafgraph I've forked your codesandbox and I think I've figured it out: https://codesandbox.io/s/polymorphic-types-required-props-for-as-forked-fmc7k?file=/src/App.tsx.

I added the OnlyIfStyleable<T> utility type that only allows elements that accept a style prop by conditionally returning T or never. The important changes are commented and marked with // NEW: ....


Edit: the error message is lacking and is not very descriptive, so perhaps there could be some improvements to the types that makes the error message nicer.

Mesoptier avatar May 13 '21 20:05 Mesoptier

Thanks @Mesoptier, that looks promising!

rafgraph avatar May 14 '21 17:05 rafgraph

@rafgraph Did you manage to get my solution working in your project?

Mesoptier avatar May 21 '21 21:05 Mesoptier