ChatGPT解决这个技术问题 Extra ChatGPT

TypeScript 和 React - 子类型?

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

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) => {/*component body*/}
你可以使用 const XyzComponent = ({ title, children }: React.PropsWithChildren<XyzComponentPropsType> => {}`

R
Ridd

只需 children: React.ReactNode


这是这里的正确答案! JSX.Element 不够好,因为有效的 React 子代可能是字符串、布尔值、null... ReactChild 出于同样的原因也不完整
@PierreFerry 问题是,几乎 anything 可以分配给 ReactNode。它在类型安全方面没有帮助,类似于将 children 输入为 any - 请参阅我的 answer 以获得解释。
PropsWithChildren
困惑...这不是OP已经拥有的吗?
@JanacMeena 这很有趣,但是当人们不知道应该将什么类型添加到儿童元素时,人们会发现这个问题。这只是 Google 上的第一个主题之一。因此,如果您不知道将什么 TS 类型添加到元素中,您可能会在谷歌上搜索“typescript react children”之类的内容,找到这个主题和第一个答案,即使问题完全是关于不同的东西:)
K
Karol Majewski

为了在 JSX 中使用 <Aux>,它需要是一个返回 ReactElement<any> | null 的函数。这就是函数组件的定义。

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

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

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

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

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

您可以使用 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
s
sunknudsen

这对我有用:

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

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


这定义不够宽泛。孩子可能是一个字符串。
至少这个例子对我有用,因为我的组件应该有 1 个或多个 JSX.Elements。谢谢!
这不适用于作为子 <MyComponent>{ condition ? <span>test</span> : null}</MyComponent> 的 JavaScript 表达式。使用 children: ReactNode 即可。
或像其他人提到的那样使用 PropsWithChildren。我个人更喜欢它而不是 children: React.ReactNode
S
Sibren

您也可以使用 React.PropsWithChildren<P>

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

这正是我一直在做的事情,我认为这是使用正确打字的更简洁的方式。
我喜欢这个解决方案,因为它是可扩展的。 type AuthProviderProps = React.PropsWithChildren<{}> 允许我封装 React 类型,并在需要时使用我自己的类型进行扩展。
最干净的方式,我正在寻找的方式
这感觉是最干净的,因为它使用了 React 自己发布的类型,所以如果在未来的 React 版本中对什么构成 react 子项的定义发生了变化,那么可以合理地假设这种类型也会被更新。在这种情况下,可能需要更新基于 ReactChild | ReactChildren 的解决方案。
G
Gapur Kassym

React Node 是以下类型之一:

布尔值(被忽略)

null 或 undefined(被忽略)

数字

细绳

一个 React 元素(JSX 的结果)

上述任何一个的数组,可能是嵌套的


j
jsina

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

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

今天的普遍共识是 React.FunctionComponent(或简写 React.FC)是 discouraged。在你的情况下,我会删除 React.FC 的使用,只做 const MyComponent = ({children}: PropsWithChildren<{}>)
C
Community

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

Minimal demonstration snippet

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

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

反应节点

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

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

interface AuxProps {
  children: ReactElement | ReactElement[]
}

例子:

鉴于 Aux 需要 React 元素作为 children,我们不小心添加了一个 string。与 ReactNode 相比,above solution 会出错 - 看看链接的 Playground。

输入的 children 对于非 JSX 属性也很有用,例如 Render Prop 回调。


P
Penny Liu

我正在使用以下

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<{}>)
propsWithChildren<{}> 然而也引入了一些代码味道,其中 {} 是任何
如果您使用 React.FC,则无需包含 children 类型,因为它已在基本 FC 类型中定义。
R
Ravi Makwana
import { ReactNode, FC } from 'react'

type Props = { children: ReactNode }

const App: FC<Props> = ({children}) => (<div>{children}</div>)

FC已经有了孩子,不需要重新声明
N
Nickofthyme

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

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

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

import React from 'react';

export default () => (
  <div children={'test'} />
);

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

https://i.stack.imgur.com/ffpa8.png

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

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

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


Control + 单击(在 Windows 上)将带您进入所述类型定义。可能是常识,但我不得不抬头。谢谢你教我们钓鱼而不是给鱼顺便说一句。
M
Mike S

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

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

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

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


f
francis

您也可以使用 JSX.ElementChildrenAttribute

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

T
Tim Iles

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

type PropsWithChildren<P> = P & { children?: ReactNode };


在这种情况下,P 是什么?
P 是组件道具的类型
是的!这很好用。 interface LayoutProps {} const Layout = ({children}: PropsWithChildren<LayoutProps>) => {...}
M
Mr.Ghamkhar

这一直对我有用:

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

我建议只使用 React.PropsWithChildren<{}> 或至少使用 React.ReactNode 而不是 JSX.Element,因为它不仅仅支持 Element 类型
这对我也有用。我认为打字稿打字现在变得混乱。
D
Denis

作为包含孩子的类型,我正在使用:

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

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

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

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

C
CodingYourLife

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

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

ع
عبد الحميد مرعي

你应该知道任何react组件都应该返回null或者React.Element,但是props.children的类型是React.ReactNode,所以需要使用Element内部的props.children来让babel配置Element的构造函数。任何 react 组件的第二条规则是命名的第一个字母应该是大写字母,以让 react 识别该组件不是 html 标签。

所以代码应该是这样的。

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

另一个提示,如果您仍在使用打字稿,功能组件应该是 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>
)

C
Champignon

这个解决方案对我来说非常好

interface Props {
    children: Array<ReactElement<ChildProps, JSXElementConstructor<ChildType>>>;
}

更新:一个全面的例子,以便更容易理解。

interface ChildProps {}

class ChildComponent extends React.Component<ChildProps> {}

interface ParentProps {
    children: Array<ReactElement<ChildProps, JSXElementConstructor<ChildComponent>>>;
}

class ParentComponent extends React.Component<ParentProps> {}

如果您的“孩子”应该在您的父组件中安全输入,这是唯一的解决方案。先生非常感谢您!
K
Kivylius

您可以创建一个简单的组件,它只输出没有 typechildren 道具或带有 FCinterface(功能组件)。您必须用空的 jsx 标记 <> 包装,因为子级可以是 undefinednull

import { FC } from "react";

export const Layout: FC = (props) => {
  return <>{props.children}</>;
};

- 或者 -

import { FC } from "react";

export const Layout: FC = ({ children }) => <>{children}</>;

I
Ishan Fernando

您还可以将 React.PropsWithChildren 扩展到包含 children 属性的界面。

interface Props extends React.PropsWithChildren{
    listItems: Items[],
    clickItem?: () => void,
    
}

或者你可以直接定义孩子

interface Props{
    listItems: Items[],
    clickItem?: () => void,
    children: React.ReactNode  
}

const List:FC<Props> = ({listItems,clickItem,children}) =>  {

  return (
    <>
    {children}
    </>
    
  )
}

或者你可以这样做。这是定义道具类型的另一种方法

const List = ({ children }: {children: React.ReactNode}) =>  {

D
Dinesh Pandiyan

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

您的 <Aux>...</Aux> 组件有两个节点 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+ 中不再是这种情况,而且如果是这种情况,错误会这样说