用于从字符串创建 JSX 元素的正确 TypeScript 类型

IT技术 javascript reactjs typescript
2021-05-20 15:03:15

我有一个组件,我想默认将其呈现为h2. 我希望消费者能够根据需要指定不同的元素。下面的代码导致错误:

TS2604 - JSX element type 'ElementType' does not have any construct or call signatures

我想我明白为什么它会失败,TS 期望渲染一个 React 节点。为清楚起见,只要变量以大写字母开头(这是 JSX 要求),React能够呈现作为字符串引用的元素。我之前在 vanilla JS + React 中成功做到了这一点,我只是不知道如何满足 TypeScript。

我怎样才能让 TypeScript 在不诉诸于的情况下呈现这个 elementType?: any

import React, {ReactNode} from 'react'

interface Props {
    children: ReactNode;
    elementType?: string;
}

export default function ({children, elementType: ElementType = 'h2'}: Props): JSX.Element {
    return (
        <ElementType>{children}</ElementType>
    );
}
4个回答

首先,介绍一下 JSX。它只是一个语法糖React.createElement,它是一个 JavaScript 表达式。

考虑到这些知识,现在让我们来看看为什么 TypeScript 会抱怨。您定义elementTypestring,但是,当您实际使用它时,它变成了一个 JavaScript 表达式。string当然类型没有任何构造或调用签名。

现在我们知道了根本原因。在 React 中,有一种类型叫做FunctionComponent. 可以猜到,它是一个函数表达式,这正是我们想要的。所以你可以定义elementTypestring | FunctionComponent这应该让 TypeScript 开心:)

仅供参考:定义props类型的推荐方法是这样做:

const MyComponent: FunctionComponent<Props> = (props) => {}

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

鉴于组件定义如下,对我有用的内容:

interface LabelProps {
        ...
        tag?: React.ElementType | string;
    }

const Label: VFC<LabelProps> = ({
       ...other props...
        tag: Element = 'span',
    }) => (
        <Element>
            {children}
        </Element>
    );

和props类型:

Label.propTypes = {
    ...
    tag: PropTypes.oneOfType([PropTypes.elementType, PropTypes.string]),
};

如果您只想要可以使用任何jsx 元素的类型

type jsxType = JSX.IntrinsicElements[keyof JSX.IntrinsicElements]

这将接受任何 jsx 元素。