I have a BoxWithAs
component, defined something like this:
const BoxWithAs = styled.div(
{
WebkitFontSmoothing: 'antialiased',
MozOsxFontSmoothing: 'grayscale'
// And more …
}
);
All fine, but now I want to deprecate one of the default props coming from @emotion/styled
, specifically the as
prop.
I did this:
type BoxWithAsType = typeof BoxWithAs;
type BoxProps = Omit<React.ComponentProps<BoxWithAsType>, 'as'>;
/**
* Component that does it all.
*/
const Box = (props: BoxProps) => {
return <BoxWithAs {...props}>{props.children}</BoxWithAs>;
};
It does remove the prop ...
... but now the component itself looses all its StyledComponent
typing information.
How can I structure this so I achieve both? My end goal is to deprecate the as
prop, and rather encourage the usage of .withComponent
(for Typescript reasons).
If we hover over BoxWithAs
we see that it is of this type:
StyledComponent<{
theme?: Theme | undefined;
as?: React.ElementType<any> | undefined;
}, React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>
So you could do:
type BoxWithoutAs = StyledComponent<{
theme?: Theme | undefined;
}, React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>;
const Box = BoxWithAs as BoxWithoutAs;
But that type is really long and unwieldy. We try simplifying it and making it a little drier:
StyledComponent<Omit<React.ComponentProps<BoxWithAsType>, "as">, React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElement>, HTMLDivElement>, {}>
That's not much better and arguably worse. What would help is if we could infer the other two parameters instead of having us typing it in full.
We can make use of a helper utility type:
type OmitProps<Component, Props extends PropertyKey> = Component extends StyledComponent<infer P, infer S, infer J> ? StyledComponent<Omit<P, Props>, S, J> : never;
Now, TypeScript will infer the other two parameters for us, and we can use Omit<P, Props>
to omit any props we wish.
const Box = BoxWithAs as OmitProps<BoxWithAsType, "as">;
Seems to work fine!