Problem Statement
Type a polymorphic Button with as prop and properly typed ref.
Explanation
Make Button default to 'button', but allow swapping the tag via as. Tie props and ref to the chosen element using a generic C extends ElementType. This keeps both props and ref fully typed for links, divs, or custom components.
Code Solution
SolutionRead Only
import React, { forwardRef } from 'react';
type PolymorphicProps<C extends React.ElementType, P> = {
as?: C;
} & Omit<React.ComponentPropsWithoutRef<C>, 'as'> & P;
type ButtonOwnProps = { variant?: 'solid' | 'ghost' };
type ButtonComponent = <C extends React.ElementType = 'button'>(
props: PolymorphicProps<C, ButtonOwnProps> & { ref?: React.ComponentPropsWithRef<C>['ref'] }
) => React.ReactElement | null;
export const Button: ButtonComponent = forwardRef(
<C extends React.ElementType = 'button'>({ as, variant = 'solid', ...rest }: PolymorphicProps<C, ButtonOwnProps>, ref: React.ComponentPropsWithRef<C>['ref']) => {
const Comp = as ?? 'button';
return <Comp ref={ref} data-variant={variant} {...rest} />;
}
) as any;
// Usage: <Button>OK</Button> | <Button as="a" href="#">Link</Button>