Proper TypeScript type for creating JSX element from string
First, a bit about JSX. It is just a syntactic sugar for React.createElement
, which is a JavaScript expression.
With this knowledge in mind, now let's take a look at why TypeScript complains. You define elementType
as string
, however, when you actually use it, it becomes a JavaScript expression. string
type of course doesn't have any construct or call signature.
Now we know the root cause. In React, there is a type called FunctionComponent
. As you can guess, it is a function expression, which is what we want. So you can define elementType
as string | FunctionComponent
. This should make TypeScript happy :)
FYI: the recommended way to define prop typing is by doing this:
const MyComponent: FunctionComponent<Props> = (props) => {}
if you just want the type of any jsx element you can use
type jsxType = JSX.IntrinsicElements[keyof JSX.IntrinsicElements]
this will accept any jsx element.
What worked for me given the component is defined like this:
interface LabelProps {
...
tag?: React.ElementType | string;
}
const Label: VFC<LabelProps> = ({
...other props...
tag: Element = 'span',
}) => (
<Element>
{children}
</Element>
);
and prop types:
Label.propTypes = {
...
tag: PropTypes.oneOfType([PropTypes.elementType, PropTypes.string]),
};
Use keyof JSX.IntrinsicElements
:
import * as React from 'react'
interface Props {
children: React.ReactNode;
elementType?: keyof JSX.IntrinsicElements;
}
export default function ({ children, elementType: ElementType = 'h2' }: Props): JSX.Element {
return (
<ElementType>{children}</ElementType>
);
}