【问题标题】:Proper TypeScript type for creating JSX element from string用于从字符串创建 JSX 元素的正确 TypeScript 类型
【发布时间】:2019-11-02 15:57:13
【问题描述】:

我有一个组件,我想默认呈现为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>
    );
}

【问题讨论】:

    标签: javascript reactjs typescript


    【解决方案1】:

    首先,关于 JSX。它只是 React.createElement 的语法糖,它是一个 JavaScript 表达式。

    有了这些知识,现在让我们来看看 TypeScript 报错的原因。您将elementType 定义为string,但是,当您实际使用它时,它变成了一个JavaScript 表达式。 string 类型当然没有任何构造或调用签名。

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

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

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

    【讨论】:

    • 这就像一个魅力!感谢分享定义道具类型的推荐方法,我还在学习TS的来龙去脉!
    【解决方案2】:

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

    【讨论】:

    • ?工作就像一个魅力!
    • 效果很好!谢谢。
    • 如果你的根元素应该是一个内在类型的最佳解决方案 - 提供自动完成和一切。
    【解决方案3】:

    鉴于组件的定义如下:

    interface LabelProps {
            ...
            tag?: React.ElementType | string;
        }
    
    const Label: VFC<LabelProps> = ({
           ...other props...
            tag: Element = 'span',
        }) => (
            <Element>
                {children}
            </Element>
        );
    

    和道具类型:

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

    【讨论】:

      【解决方案4】:

      如果你只想要 any jsx 元素的类型,你可以使用

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

      这将接受任何 jsx 元素。

      【讨论】:

        【解决方案5】:

        那不行,你需要使用 React.createElement()。 https://reactjs.org/docs/react-api.html#createelement

        像这样的

          import React, {ReactNode} from 'react'
        
        interface Props {
            children: ReactNode,
            elementType?: string,
        }
        
        export default function ({children, elementType: ElementType = 'h2'}: Props): JSX.Element {
        
            return React.createElement(elementType, {}, children); 
        }
        

        【讨论】:

        • 恕我直言,这确实有效。我发布的只是您解决方案的语法糖。这不是 React 的问题,而是 TS 的问题。
        猜你喜欢
        • 2020-09-16
        • 2011-04-09
        • 2019-01-02
        • 1970-01-01
        • 2020-01-14
        • 2019-10-14
        • 1970-01-01
        • 2017-11-24
        • 1970-01-01
        相关资源
        最近更新 更多