From 2e5243e2e7bed33be1ab4ce353091c2a1d03e5cb Mon Sep 17 00:00:00 2001 From: Nuey San Waldman Date: Fri, 1 Dec 2023 11:20:16 +0100 Subject: [PATCH] Add PolymorphicComponentProps --- src/index.ts | 1 + .../PolymorphicComponentProps.mdx | 83 +++++++++++++++++++ .../PolymorphicComponentProps.ts | 25 ++++++ 3 files changed, 109 insertions(+) create mode 100644 src/types/PolymorphicComponentProps/PolymorphicComponentProps.mdx create mode 100644 src/types/PolymorphicComponentProps/PolymorphicComponentProps.ts diff --git a/src/index.ts b/src/index.ts index 63b73c4..2f04adb 100644 --- a/src/index.ts +++ b/src/index.ts @@ -35,6 +35,7 @@ export * from './lifecycle/hooks/useIsMountedState/useIsMountedState.js'; export * from './lifecycle/hooks/useMount/useMount.js'; export * from './lifecycle/hooks/useUnmount/useUnmount.js'; export * from './nextjs/useIsomorphicLayoutEffect/useIsomorphicLayoutEffect.js'; +export * from './types/PolymorphicComponentProps/PolymorphicComponentProps.js'; export * from './utils/arrayRef/arrayRef.js'; export * from './utils/createTimeout/createTimeout.js'; export * from './utils/isRefObject/isRefObject.js'; diff --git a/src/types/PolymorphicComponentProps/PolymorphicComponentProps.mdx b/src/types/PolymorphicComponentProps/PolymorphicComponentProps.mdx new file mode 100644 index 0000000..1d1d27e --- /dev/null +++ b/src/types/PolymorphicComponentProps/PolymorphicComponentProps.mdx @@ -0,0 +1,83 @@ +import { Meta } from '@storybook/blocks'; + + + +# PolymorphicComponentProps + +A collection of generic types to help build strongly typed Polymorphic Components, consistently. + +`PolymorphicComponentProps` takes care of combining your Component's unique prop types with those of +any HTML element or React Component you happen to be assigning to your Polymorphic Component. As an +extra precaution, a check is performed to omit any "default" prop type from the chosen element that +might conflict with one of the same name in your Component. + +## Usage + +```tsx +type FooComponentProps = PolymorphicComponentProps< + T, + { barProp: string | undefined } +>; + +type FooComponent = (props: FooComponentProps) => ReactElement | null; + +export const Foo: FooComponent = ({ + as: Component = 'div', + children, + ...otherProps +}: FooComponentProps) => { + return {children}; +}; +``` + +The type `FooComponentProps` consists of `T` and any custom prop types you require for +`` (in this example, `barProp`). The types for `T` are derived from the element in +the `as` prop: + +```tsx + + This is a proper link + + + + Foo as a heading: anything goes. + +``` + +So while two examples use the same FooComponent, their `FooComponent` types are not the same. They +are derived from the types from their respective `` and `

` elements. + +```tsx + + This button will throw an error + + + console.log("Clicked")}> + This button is okay + +``` + +### Using Refs + +A Polymorphic Component with Refs can be typed with `PolymorphicComponentPropsWithRef` and +`PolymorphicRef`: + +```tsx +type FooComponentProps = PolymorphicComponentPropsWithRef< + T, + { barProp: string | undefined } +>; + +type FooComponent = (props: FooComponentProps) => ReactElement | null; + +export const Foo: FooComponent = ( + { as: Component = 'div', children, ...otherProps }: FooComponentProps, + ref?: PolymorphicRef, +) => { + return ( + + {children} + + ); +}; +``` diff --git a/src/types/PolymorphicComponentProps/PolymorphicComponentProps.ts b/src/types/PolymorphicComponentProps/PolymorphicComponentProps.ts new file mode 100644 index 0000000..da93e8f --- /dev/null +++ b/src/types/PolymorphicComponentProps/PolymorphicComponentProps.ts @@ -0,0 +1,25 @@ +import type { + ComponentPropsWithoutRef, + ComponentPropsWithRef, + ElementType, + PropsWithChildren, +} from 'react'; + +/** + * AsProp + */ +type AsProp = { as?: T }; + +type PropsToOmit = keyof (AsProp & P); + +export type PolymorphicComponentProps< + T extends ElementType, + P = Record, +> = PropsWithChildren

> & Omit, PropsToOmit>; + +export type PolymorphicRef = ComponentPropsWithRef['ref']; + +export type PolymorphicComponentPropsWithRef< + T extends ElementType, + P = Record, +> = PolymorphicComponentProps & { ref?: PolymorphicRef };