【问题标题】:TypeScript and React - children type?TypeScript 和 React - 子类型?
【发布时间】:2019-05-10 08:43:13
【问题描述】:

我有一个非常简单的功能组件如下:

import * as React from 'react';

export interface AuxProps  { 
    children: React.ReactNode
 }


const aux = (props: AuxProps) => props.children;

export default aux;

还有另一个组件:

import * as React from "react";

export interface LayoutProps  { 
   children: React.ReactNode
}

const layout = (props: LayoutProps) => (
    <Aux>
        <div>Toolbar, SideDrawer, Backdrop</div>
        <main>
            {props.children}
        </main>
    <Aux/>
);

export default layout;

我不断收到以下错误:

[ts] JSX 元素类型“ReactNode”不是 JSX 元素的构造函数。 类型“未定义”不可分配给类型“ElementClass”。 [2605]

如何正确输入?

【问题讨论】:

  • 为了避免重新发明轮子,你也可以使用 React.FC 来定义你的功能组件。即const layout React.FC = (props) =&gt; {/*component body*/}

标签: reactjs typescript jsx typescript2.0


【解决方案1】:

React 组件应该有一个包装节点或返回一个节点数组。

您的&lt;Aux&gt;...&lt;/Aux&gt; 组件有两个节点divmain

尝试将您的孩子包装在 div in Aux 组件中。

import * as React from 'react';

export interface AuxProps  { 
  children: React.ReactNode
}

const aux = (props: AuxProps) => (<div>{props.children}</div>);

export default aux;

【讨论】:

  • 不正确。在 React 16+ 中不再是这种情况,而且如果是这种情况,错误就会这样说
【解决方案2】:

为了在你的 JSX 中使用 &lt;Aux&gt;,它必须是一个返回 ReactElement&lt;any&gt; | null 的函数。这就是函数组件的定义。

但是,它目前被定义为返回React.ReactNode 的函数,这是一个更广泛的类型。正如 React 类型所说:

type ReactNode = ReactChild | ReactFragment | ReactPortal | boolean | null | undefined;

通过将返回值包装到 React Fragment (&lt;&gt;&lt;/&gt;) 中,确保消除不需要的类型:

const aux: React.FC<AuxProps> = props =>
  <>{props.children}</>;

【讨论】:

  • 我不明白为什么需要将 children 包装在 React 片段中。介意解释吗?在 React 中,return children; 是完全有效的。
  • 如果children 是一个数组,则不会。如果是这种情况,您需要将它们包装在单个节点中或使用 React Fragments。
  • 是的,在我的代码中我有:return React.Children.count(children) &gt; 1 ? &lt;&gt;{children}&lt;/&gt; : children,但打字稿抱怨这一点。我将其替换为 &lt;&gt;{children}&lt;/&gt;
  • 它抱怨是因为children 是一个恰好只包含一项的元素列表。我认为如果你这样做return React.Children.count(children) &gt; 1 ? &lt;&gt;{children}&lt;/&gt; : children[0]@sanfilippopablo 会起作用
  • 这似乎不适用于较新版本的 React。我在控制台登录并可以确认我有一个儿童道具,但即使 &lt;React.Fragment&gt; 包围 {props.children} 我也没有返回任何东西。
【解决方案3】:

这对我有用:

interface Props {
  children: JSX.Element[] | JSX.Element
}

编辑我建议现在改用children: React.ReactNode

【讨论】:

  • 这个定义不够宽泛。孩子可以是一个字符串。
  • 至少这个例子对我有用,因为我的组件应该期望 1 个或多个 JSX.Elements。谢谢!
  • 这不适用于作为子 &lt;MyComponent&gt;{ condition ? &lt;span&gt;test&lt;/span&gt; : null}&lt;/MyComponent&gt; 的 JavaScript 表达式。使用children: ReactNode 将起作用。
  • 或使用PropsWithChildren,正如其他人提到的那样。我个人更喜欢children: React.ReactNode
【解决方案4】:

来自 TypeScript 网站:https://github.com/Microsoft/TypeScript/issues/6471

推荐的做法是将 props 类型写为 {children?: 任何}

这对我有用。子节点可以是许多不同的东西,因此显式键入可能会遗漏案例。

这里对后续问题进行了更长的讨论:https://github.com/Microsoft/TypeScript/issues/13618,但 any 方法仍然有效。

【讨论】:

    【解决方案5】:

    只需children: React.ReactNode

    【讨论】:

    • 这是正确的答案! JSX.Element 不够好,因为有效的 React 子代可能是字符串、布尔值、null...ReactChild 也是不完整的,原因相同
    • @PierreFerry 问题是,几乎任何东西都可以分配给ReactNode。它在类型安全方面没有帮助,类似于将children 输入为any - 请参阅我的answer 以获得解释。
    • 感谢您的回复@ford04。在我的用例中,我希望孩子们打字松散。我的组件接受任何类型的有效 React 子级。所以我相信这仍然是我一直在寻找的答案:)
    • PropsWithChildren
    • 困惑...这不是OP已经拥有的吗?
    【解决方案6】:

    你可以这样声明你的组件:

    const MyComponent: React.FunctionComponent = (props) => {
        return props.children;
    }
    

    【讨论】:

    • 今天的普遍共识是 React.FunctionComponent(或简写 React.FC)是discouraged。在你的情况下,我会删除 React.FC 的使用,只做 const MyComponent = ({children}: PropsWithChildren&lt;{}&gt;)
    【解决方案7】:

    查找任何类型的通用方法是通过示例。 typescript 的美妙之处在于您可以访问所有类型,只要您拥有正确的@types/ 文件。

    为了自己回答这个问题,我只是想到了一个组件 react 使用具有 children 属性。首先想到的是? &lt;div /&gt; 怎么样?

    您需要做的就是打开 vscode 并使用 @types/react 在 React 项目中创建一个新的 .tsx 文件。

    import React from 'react';
    
    export default () => (
      <div children={'test'} />
    );
    
    

    将鼠标悬停在 children 属性上会显示类型。你知道什么——它的类型是ReactNode(不需要ReactNode[])。

    然后,如果您单击类型定义,它会将您直接带到来自DOMAttributes 接口的children 的定义。

    // node_modules/@types/react/index.d.ts
    interface DOMAttributes<T> {
      children?: ReactNode;
      ...
    }
    

    注意:此过程应用于查找任何未知类型!他们都在那里等你找到他们:)

    【讨论】:

    • Control + 单击(在 Windows 上)带您进入所述类型定义。可能是常识,但我不得不抬头。谢谢你教我们钓鱼,而不是顺便给鱼。
    【解决方案8】:

    作为包含子元素的类型,我使用的是:

    type ChildrenContainer = Pick<JSX.IntrinsicElements["div"], "children">
    

    这种子容器类型足够通用,可以支持所有不同的情况,并且与 ReactJS API 保持一致。

    因此,对于您的示例,它将类似于:

    const layout = ({ children }: ChildrenContainer) => (
        <Aux>
            <div>Toolbar, SideDrawer, Backdrop</div>
            <main>
                {children}
            </main>
        <Aux/>
    )
    

    【讨论】:

      【解决方案9】:

      函数组件返回类型在 TypeScript 中被限制为JSXElement | null。这是当前的类型限制,纯 React allows more 返回类型。

      Minimal demonstration snippet

      您可以使用类型断言或片段作为解决方法:

      const Aux = (props: AuxProps) => <>props.children</>; 
      const Aux2 = (props: AuxProps) => props.children as ReactElement; 
      

      ReactNode

      如果目标是为Aux 提供强类型,children: React.ReactNode 可能不是最理想的。

      几乎任何东西都可以分配给当前的ReactNode 类型,相当于{} | undefined | null。对于您的情况,更安全的类型可能是:

      interface AuxProps {
        children: ReactElement | ReactElement[]
      }
      

      例子:

      鉴于 Aux 需要 React 元素为 children,我们不小心添加了一个 string 到它。然后above solutionReactNode 相比会出错 - 看看链接的游乐场。

      键入的 children 也可用于非 JSX 道具,例如 Render Prop 回调。

      【讨论】:

        【解决方案10】:

        这些答案似乎已经过时 - React 现在有一个内置类型 PropsWithChildren&lt;{}&gt;。它的定义类似于此页面上的一些正确答案:

        type PropsWithChildren&lt;P&gt; = P &amp; { children?: ReactNode };

        【讨论】:

        • 在这种情况下P 是什么?
        • P 是你的组件道具的类型
        • 是的!这很好用。 interface LayoutProps {} const Layout = ({children}: PropsWithChildren&lt;LayoutProps&gt;) =&gt; {...}
        【解决方案11】:

        您可以使用ReactChildrenReactChild

        import React, { ReactChildren, ReactChild } from 'react';
         
        interface AuxProps {
          children: ReactChild | ReactChildren;
        }
        
        const Aux = ({ children }: AuxProps) => (<div>{children}</div>);
        
        export default Aux;
        

        如果您需要传递元素的平面数组:

        interface AuxProps {
          children: ReactChild | ReactChild[] | ReactChildren | ReactChildren[];
        }
        

        【讨论】:

        • 对于任何来到这里的人来说,这是错误的。 ReactChildren 不等于 ReactChild[],因为它是实用函数的类型。 reactjs.org/docs/react-api.html#reactchildren。如果您使用 ReactChild | ReactChildren ,您将无法将孩子作为数组传递。
        • @kay 谢谢你。我在答案中添加了平面数组定义。它也应该涵盖您的情况。告诉我。
        • 谢谢!但是,我不认为ReactChildren 是正确的类型。我认为children: ReactChild | ReactChild[] 就足够了,或者只是ReactNode
        • 我不同意。与ReactChildReactChildren 相比,创建类型/接口为ReactNode 更好,因为它不仅接受ReactChild/ReactChildren。或者更好的是,像其他人提到的那样使用PropsWithChildren
        【解决方案12】:

        您也可以使用React.PropsWithChildren&lt;P&gt;

        type ComponentWithChildProps = React.PropsWithChildren<{example?: string}>;
        

        【讨论】:

        • 这正是我一直在做的,我认为这是使用正确打字的更简洁的方式。
        • 我喜欢这个解决方案,因为它是可扩展的。 type AuthProviderProps = React.PropsWithChildren&lt;{}&gt; 允许我封装 React 类型,并在需要时使用我自己的类型进行扩展。
        • 最干净的方式,我一直在寻找的方式
        【解决方案13】:

        这一直对我有用:

        type Props = {
          children: JSX.Element;
        };
        

        【讨论】:

        • 我建议只使用React.PropsWithChildren&lt;{}&gt; 或至少使用React.ReactNode 而不是JSX.Element,因为它不仅仅支持Element 类型
        • 这对我也有用。我认为打字稿现在变得混乱了。
        【解决方案14】:

        React Node 是以下类型之一:

        • Boolean(被忽略)
        • nullundefined(被忽略)
        • Number
        • String
        • React elementJSX 的结果)
        • 上述任一数组,可能是嵌套数组

        【讨论】:

          【解决方案15】:

          我正在使用以下

          type Props = { children: React.ReactNode };
          
          const MyComponent: React.FC<Props> = ({children}) => {
            return (
              <div>
                { children }
              </div>
            );
          
          export default MyComponent;
          

          【讨论】:

          • 今天的普遍共识是 React.FunctionComponent(或简写 React.FC)是discouraged。在你的情况下,我会删除 React.FC 的使用,只做 const MyComponent = ({children}: PropsWithChildren&lt;{}&gt;)
          • propsWithChildren 但是也引入了一些代码气味,其中 {} 为任意
          • 如果您使用React.FC,则无需包含children 类型,因为它已在基本FC 类型中定义。
          【解决方案16】:

          你也可以使用JSX.ElementChildrenAttribute

          export default function Layout({children}: JSX.ElementChildrenAttribute) {
              return <div>
                  {children}
              </div>
          }
          

          【讨论】:

            【解决方案17】:
            1. 你应该知道任何 react 组件应该返回 nullReact.Element,但是 props.children 的类型是 React.ReactNode,所以你需要在一个内部使用 props.children Element让babel配置Element的构造函数。

            2. 任何 react 组件的第二条规则是命名的第一个字母应该是大写字母,以便 react 识别该组件不是 html 标签。

            所以代码应该是这样的。

            const Aux = (props: AuxProps) => <>props.children</>;
            

            另一个提示,如果你还在使用 typescript,功能组件应该是 React.FC 这样的类型

            type Props = {
               title: string;
            }
            
            const Aux:React.FC<Props> = (props) =>
            (
                <div>
                    <h3>{props.title}</h3>
                    { props.children }
                    {/* children is exist by default in type React.FC */}
                </div>
            )
            

            【讨论】:

              【解决方案18】:
              import { ReactNode, FC } from 'react'
              
              type Props = { children: ReactNode }
              
              const App: FC<Props> = ({children}) => (<div>{children}</div>)
              

              【讨论】:

                【解决方案19】:

                对我来说@Sibren 的回答不够清楚,但我找到了this SO anwer 并将其全部内联(这可能不是最短的方法,但我觉得最容易掌握)。

                function MyComponentWithChildren({
                    customProp,
                    children, /*notice the children are implicit*/
                }: React.PropsWithChildren<{ customProp: any }>) {
                    return <div>{children}</div>;
                }
                

                【讨论】:

                  猜你喜欢
                  • 2021-03-30
                  • 1970-01-01
                  • 2022-08-07
                  • 2018-12-27
                  • 1970-01-01
                  • 2020-03-19
                  • 2021-01-20
                  • 2018-03-01
                  • 2020-05-25
                  相关资源
                  最近更新 更多