kripod / react-polymorphic-types

Zero-runtime polymorphic component definitions for React
MIT License
195 stars 8 forks source link

Passing a generic component to `as` fails #15

Open amannn opened 1 year ago

amannn commented 1 year ago

I've got a polymorphic button component that looks like this:

import type {PolymorphicPropsWithoutRef} from 'react-polymorphic-types';

const defaultElement = 'button';

type ButtonOwnProps = {
  color?: string;
};

type ButtonProps<T extends React.ElementType = typeof defaultElement> =
  PolymorphicPropsWithoutRef<ButtonOwnProps, T>;

function Button<T extends React.ElementType = typeof defaultElement>({
  as,
  color,
  ...rest
}: ButtonProps<T>) {
  const Element: React.ElementType = as || defaultElement;
  return <Element style={{color}} {...rest} />;
}

Simple cases work:

// ✅ Works
<Button as="a" href="/test">
  Two
</Button>;

… but when I pass a component to the as prop that is itself generic, I get an error.

function Test<Name extends 'one' | 'two'>({test}: {test: Name}) {
  return <>{test}</>;
}

// ❌ Fails
<Button as={Test} color="rebeccapurple" test="two">
  One
</Button>;
Type '{ children: string; as: <Name extends "one" | "two">({ test }: { test: Name; }) => Element; color: string; test: "two"; }' is not assignable to type 'IntrinsicAttributes & Omit<{ test: "one" | "two"; }, "as" | "color"> & ButtonOwnProps & { as?: (<Name extends "one" | "two">({ test }: { test: Name; }) => Element) | undefined; }'.
  Property 'children' does not exist on type 'IntrinsicAttributes & Omit<{ test: "one" | "two"; }, "as" | "color"> & ButtonOwnProps & { as?: (<Name extends "one" | "two">({ test }: { test: Name; }) => Element) | undefined; }'.ts(2322)

Can you provide some help for this use case @kripod? Thank you very much!

amannn commented 1 year ago

I believe it's this todo in the code base: https://github.com/kripod/react-polymorphic-types/blob/30ba44854a4edec6dcb5ae797c6df4e849ab73db/index.d.ts#L25

kripod commented 7 months ago

A component passed to as shouldn’t have any required props. Otherwise, specifying a generic component should be possible using a different approach for polymorphism as shown here.