TypeScript and React - Kinder tippen?


136

Ich habe eine sehr einfache Funktionskomponente wie folgt:

import * as React from 'react';

export interface AuxProps  { 
    children: React.ReactNode
 }


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

export default aux;

Und noch eine Komponente:

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;

Ich erhalte immer wieder den folgenden Fehler:

[ts] Der JSX-Elementtyp 'ReactNode' ist keine Konstruktorfunktion für JSX-Elemente. Der Typ 'undefined' kann nicht dem Typ 'ElementClass' zugewiesen werden. [2605]

Wie tippe ich das richtig?

Antworten:


131

Gerade children: React.ReactNode


Das ist hier die richtige Antwort! JSX.Elementist nicht gut genug, da ein gültiges React-Kind eine Zeichenfolge sein kann, ein Boolescher ReactChild
Pierre Ferry

2
@PierreFerry Das Problem ist, dass fast alles zugewiesen werden kann ReactNode. Es hilft nicht in Bezug auf die Typensicherheit, ähnlich wie beim Tippen childrenals any- siehe meine Antwort für eine Erklärung.
ford04

Vielen Dank für Ihre Antwort @ ford04. In meinem Anwendungsfall möchte ich, dass Kinder lose tippen. Meine Komponente akzeptiert alle gültigen React-Kinder. Ich glaube, das ist immer noch die Antwort, nach der ich gesucht habe :)
Pierre Ferry

47

Um <Aux>in Ihrem JSX verwendet zu werden, muss es eine Funktion sein, die zurückgibt ReactElement<any> | null. Das ist die Definition einer Funktionskomponente .

Derzeit ist es jedoch als eine Funktion definiert, die zurückgibt React.ReactNode, was ein viel breiterer Typ ist. Wie React-Eingaben sagen:

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

Stellen Sie sicher, dass die unerwünschten Typen neutralisiert werden, indem Sie den zurückgegebenen Wert in React Fragment ( <></>) einschließen :

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

Ich verstehe nicht, warum es erforderlich ist, childrenein Reaktionsfragment einzuschließen. Möchtest du das erklären? In React ist es vollkommen gültig, nur return children;.
Sanfilippopablo

1
Nicht wenn childrenes sich um ein Array handelt. Wenn dies der Fall ist, müssen Sie sie entweder in einen einzelnen Knoten einschließen oder "Fragmente reagieren" verwenden.
Karol Majewski

Ja, in meinem Code habe ich return React.Children.count(children) > 1 ? <>{children}</> : children:, aber Typoskript beschwert sich darüber. Ich habe es durch nur ersetzt <>{children}</>.
Sanfilippopablo

2
Es beschwert sich, weil childrenes sich um eine Liste von Elementen handelt, die zufällig nur 1 Element enthalten. Ich denke, es würde funktionieren, wenn Sie return React.Children.count(children) > 1 ? <>{children}</> : children[0]@sanfilippopablo
tamj0rd2

Dies scheint in neueren Versionen von React nicht zu funktionieren. Ich habe mich an der Konsole angemeldet und kann bestätigen, dass ich eine Kinderstütze habe, aber selbst in der <React.Fragment>Umgebung {props.children}gebe ich nichts zurück.
Mark

34

Das hat bei mir funktioniert:

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

Bearbeiten Ich würde empfehlen, children: React.ReactNodestattdessen jetzt zu verwenden.


8
Die Definition ist nicht breit genug. Das Kind könnte eine Schnur sein.
Mike S

Zumindest hat dieses Beispiel für mich funktioniert, da meine Komponente 1 oder mehr JSX.Elements erwarten sollte. Vielen Dank!
Italo Borges

1
Dies funktioniert nicht mit JavaScript-Ausdrücken als untergeordnete Elemente <MyComponent>{ condition ? <span>test</span> : null}</MyComponent>. Verwenden children: ReactNodewird funktionieren.
Nickofthyme

10

Sie können Ihre Komponente folgendermaßen deklarieren:

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

8

Sie können verwenden ReactChildrenund ReactChild:

import React, { ReactChildren, ReactChild } from 'react';

interface AuxProps {
  children: ReactChild | ReactChildren;
}

const Aux = ({ children }: AuxProps) => (<div>{children}</div>);

export default Aux;

6

Von der TypeScript-Site: https://github.com/Microsoft/TypeScript/issues/6471

Die empfohlene Vorgehensweise besteht darin, den Requisitentyp als {Kinder?: Beliebig} zu schreiben.

Das hat bei mir funktioniert. Der untergeordnete Knoten kann viele verschiedene Dinge sein, so dass bei der expliziten Eingabe Fälle übersehen werden können.

Es gibt hier eine längere Diskussion zum Folgeproblem: https://github.com/Microsoft/TypeScript/issues/13618 , aber jeder Ansatz funktioniert immer noch.


5

Der Rückgabetyp der Funktionskomponente ist JSXElement | nullin TypeScript beschränkt. Dies ist eine aktuelle Typbeschränkung. Pure React ermöglicht mehr Rückgabetypen.

Minimaler Demonstrationsausschnitt

Sie können entweder eine Typzusicherung oder Fragmente als Problemumgehung verwenden:

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

ReactNode

children: React.ReactNodekönnte suboptimal sein, wenn das Ziel darin besteht, starke Typen für zu haben Aux.

Dem aktuellen ReactNodeTyp kann fast alles zugeordnet werden , was äquivalent zu ist {} | undefined | null. Ein sicherer Typ für Ihren Fall könnte sein:

interface AuxProps {
  children: ReactElement | ReactElement[]
}

Beispiel:

Gegebene AuxBedürfnisse Reagieren Sie Elemente als children, wir haben versehentlich ein hinzugefügt string. Dann würde die obige Lösung im Gegensatz zu ReactNode- werfen Sie einen Blick auf die verknüpften Spielplätze.

Typisiert childrensind auch nützlich für Nicht-JSX-Requisiten wie einen Render Prop- Rückruf.


5

Sie können auch verwenden React.PropsWithChildren<P>.

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

3

Der allgemeine Weg, einen Typ zu finden, ist beispielhaft. Das Schöne an Typoskript ist, dass Sie Zugriff auf alle Typen haben, solange Sie über die richtigen @types/Dateien verfügen .

Um dies selbst zu beantworten, dachte ich nur an eine Komponente, die die childrenRequisite verwendet. Das erste, was mir in den Sinn kam? Wie wäre es mit einem <div />?

Alles was Sie tun müssen, ist vscode zu öffnen und eine neue .tsxDatei in einem Reaktionsprojekt mit zu erstellen @types/react.

import React from 'react';

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

Wenn childrenSie mit der Maus über die Requisite fahren, sehen Sie den Typ. Und was weißt du - sein Typ ist ReactNode(keine Notwendigkeit für ReactNode[]).

Geben Sie hier die Bildbeschreibung ein

Dann , wenn Sie die Typdefinition klicken hinein bringt Sie direkt in die Definition des Begriffs childrenkommen DOMAttributesSchnittstelle.

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

Hinweis: Dieser Prozess sollte verwendet werden, um einen unbekannten Typ zu finden! Alle warten nur darauf, dass du sie findest :)


2

Als Typ, der Kinder enthält, verwende ich:

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

Dieser untergeordnete Containertyp ist generisch genug, um alle verschiedenen Fälle zu unterstützen, und auch an der ReactJS-API ausgerichtet.

Für Ihr Beispiel wäre es also ungefähr so:

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

1

Diese Antworten scheinen veraltet zu sein - React verfügt jetzt über einen integrierten Typ PropsWithChildren<{}>. Es ist ähnlich wie einige der richtigen Antworten auf dieser Seite definiert:

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


1
Was ist Pbei diesem Typ?
Sunpietro

Pist der Typ der Requisiten Ihrer Komponente
Tim Iles

-2

Reaktionskomponenten sollten einen einzelnen Wrapper-Knoten haben oder ein Array von Knoten zurückgeben.

Ihre <Aux>...</Aux>Komponente hat zwei Knoten divund main.

Versuchen Sie, Ihre Kinder in eine divIn- AuxKomponente zu wickeln .

import * as React from 'react';

export interface AuxProps  { 
  children: React.ReactNode
}

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

export default aux;

1
Nicht wahr. In React 16+ ist dies nicht mehr der Fall, und der Fehler würde dies sagen, wenn es der Fall wäre
Andrew Li
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.