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>
  );
}