Skip to content

Instantly share code, notes, and snippets.

@kripod
Last active March 27, 2024 18:14
Show Gist options
  • Star 47 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save kripod/4434e7cecfdecee160026aee49ea6ee8 to your computer and use it in GitHub Desktop.
Save kripod/4434e7cecfdecee160026aee49ea6ee8 to your computer and use it in GitHub Desktop.
Superseded by https://www.kripod.dev/blog/behind-the-as-prop-polymorphism-done-well/ – Polymorphic `as` prop for React components with TypeScript
import React from 'react';
// Source: https://github.com/emotion-js/emotion/blob/master/packages/styled-base/types/helper.d.ts
type PropsOf<
// eslint-disable-next-line @typescript-eslint/no-explicit-any
E extends keyof JSX.IntrinsicElements | React.JSXElementConstructor<any>
> = JSX.LibraryManagedAttributes<E, React.ComponentPropsWithRef<E>>;
export interface BoxOwnProps<E extends React.ElementType = React.ElementType> {
as?: E;
}
export type BoxProps<E extends React.ElementType> = BoxOwnProps<E> &
Omit<PropsOf<E>, keyof BoxOwnProps>;
export type PolymorphicComponentProps<E extends React.ElementType, P> = P &
BoxProps<E>;
const defaultElement = 'div';
export const Box = React.forwardRef(
({ as, ...restProps }: BoxOwnProps, ref: React.Ref<Element>) => {
const Element = as || defaultElement;
return <Element ref={ref} {...restProps} />;
},
) as <E extends React.ElementType = typeof defaultElement>(
props: BoxProps<E>,
) => JSX.Element;
/* Usage example: */
interface CustomComponentOwnProps {
customProp?: number;
onClick: boolean;
}
type CustomComponentProps<
E extends React.ElementType
> = PolymorphicComponentProps<E, CustomComponentOwnProps>;
function CustomComponent<E extends React.ElementType>(
props: CustomComponentProps<E>,
): JSX.Element {
return <Box {...props} />;
}
@mbaneshi
Copy link

mbaneshi commented Aug 1, 2022

Great

@wojtekmaj
Copy link

Hmmm... I'm getting an error that Box.displayName is not specified, but when I add it, then TypeScript complains. Same goes when I add PropTypes...

@vfshera
Copy link

vfshera commented Oct 26, 2023

How can I limit as to specific elements like h1 | h2 | h3 | P only?

@kripod
Copy link
Author

kripod commented Oct 26, 2023

@vfshera you may swap extends React.ElementType instances for extends "h1" | "h2" | "h3" | "p".

@kripod
Copy link
Author

kripod commented Mar 22, 2024

@vfshera I recently wrote an article about polymorphism in React, which embraces narrowing the set of built-in elements endorsed as a performance improvement.

@vfshera
Copy link

vfshera commented Mar 27, 2024

thanks checking it out

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment