So I want to have a component which takes an as prop, and from that creates an element. In code, this is simple enough:
function Box({ as = 'div', children, ...props }) {
return createElement(as, props, children);
}
I can derive the props for a given as using the following basic code:
type Elements = JSX.IntrinsicElements;
type ElementType = keyof Elements | ComponentType<any>;
type ElementProps<TElement> = TElement extends keyof Elements
? Elements[TElement]
: TElement extends ComponentType<infer TProps>
? TProps
: never;
type Props<TElement> = {
readonly as: TElement;
} & ElementProps<TElement>;
type DivProps = Props<{as: 'div'}>; // this works
However, what I'm having problems with is forwarding the ref.
I can do the following to convert the literal string into an HTMLElement
type InferElement<TElement> = TElement extends keyof HTMLElementTagNameMap
? HTMLElementTagNameMap[TElement]
: TElement;
type DivElement = InferElement<'div'>; // HTMLDivElement
But I can't seem to get it to work when forwarding the ref:
function BaseBox<T extends ElementType>(
{ as = 'div', children, ...props }: Props<T>,
ref: ForwardedRef<InferElement<T>>
) {
return createElement(as, props, children);
}
const Box = forwardRef(BaseBox); // this doesn't work
How would I go about telling typescript how to interpret this correctly?