TypeScript dan React - tipe anak-anak?

138

Saya memiliki komponen fungsional yang sangat sederhana sebagai berikut:

import * as React from 'react';

export interface AuxProps  { 
    children: React.ReactNode
 }


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

export default aux;

Dan komponen lainnya:

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;

Saya terus mendapatkan kesalahan berikut:

[ts] Jenis elemen JSX 'ReactNode' bukanlah fungsi konstruktor untuk elemen JSX. Ketik 'undefined' tidak dapat digunakan untuk mengetik 'ElementClass'. [2605]

Bagaimana cara saya mengetik ini dengan benar?

Asool
sumber

Jawaban:

133

Hanya children: React.ReactNode

Ridd
sumber
1
Inilah jawaban yang benar di sini! JSX.Elementtidak cukup baik karena anak-anak React yang valid dapat berupa string, boolean, null ... ReactChildjuga tidak lengkap karena alasan yang sama
Pierre Ferry
2
@PierreFerry Masalahnya adalah, hampir semua hal bisa ditugaskan ReactNode. Itu tidak membantu dalam hal keamanan tipe, mirip dengan mengetik childrensebagai any- lihat jawaban saya untuk penjelasan.
ford04
Terima kasih atas tanggapan Anda @ ford04. Dalam kasus penggunaan saya, saya ingin anak-anak menjadi tipe yang longgar. Komponen saya menerima semua jenis turunan React yang valid. Jadi saya yakin ini masih jawaban yang saya cari :)
Pierre Ferry
47

Untuk dapat digunakan <Aux>di JSX Anda, ini harus berupa fungsi yang mengembalikan ReactElement<any> | null. Itulah definisi komponen fungsi .

Namun, saat ini didefinisikan sebagai fungsi yang mengembalikan React.ReactNode, yang merupakan tipe yang jauh lebih luas. Seperti yang dikatakan pengetikan React:

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

Pastikan tipe yang tidak diinginkan dinetralkan dengan membungkus nilai yang dikembalikan ke dalam React Fragment ( <></>):

const aux: React.FC<AuxProps> = props =>
  <>{props.children}</>;
Karol Majewski
sumber
Saya tidak mengerti mengapa itu perlu dibungkus childrendalam Fragmen React. Mau menjelaskan? Di React, ini sangat valid untuk hanya return children;.
sanfilippopablo
1
Tidak jika childrenadalah sebuah array. Jika demikian, Anda perlu membungkusnya dalam satu node atau menggunakan React Fragments.
Karol Majewski
Ya, dalam kode saya, saya punya return React.Children.count(children) > 1 ? <>{children}</> : children:, tapi naskah ketikan mengeluh tentang itu. Saya menggantinya dengan hanya <>{children}</>.
sanfilippopablo
2
Ia mengeluh karena childrenmerupakan daftar elemen yang kebetulan hanya berisi 1 item. Saya pikir itu akan berhasil jika Anda melakukannya return React.Children.count(children) > 1 ? <>{children}</> : children[0]@sanfilippopablo
tamj0rd2
Ini tampaknya tidak berfungsi di versi React yang lebih baru. Saya konsol login dan dapat mengonfirmasi bahwa saya memiliki prop anak, tetapi bahkan dengan <React.Fragment>sekitarnya {props.children}saya tidak mengembalikan apa pun.
Tandai
34

Inilah yang berhasil untuk saya:

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

Edit Saya akan merekomendasikan menggunakan children: React.ReactNodesebagai gantinya sekarang.

sunknudsen
sumber
8
Definisi itu tidak cukup luas. Anak itu bisa menjadi tali.
Mike S
Setidaknya contoh ini berhasil untuk saya karena komponen saya diharapkan mengharapkan 1 atau lebih JSX.Elements. Terima kasih!
Italo Borges
1
Ini tidak akan berfungsi dengan ekspresi JavaScript sebagai anak-anak <MyComponent>{ condition ? <span>test</span> : null}</MyComponent>. Menggunakan children: ReactNodeakan berhasil.
Nickofthyme
10

Anda dapat mendeklarasikan komponen Anda seperti ini:

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

Anda dapat menggunakan ReactChildrendan ReactChild:

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

interface AuxProps {
  children: ReactChild | ReactChildren;
}

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

export default Aux;
Wilk
sumber
6

Dari situs TypeScript: https://github.com/Microsoft/TypeScript/issues/6471

Praktik yang direkomendasikan adalah menulis jenis alat peraga sebagai {children ?: any}

Itu berhasil untuk saya. Node anak dapat terdiri dari banyak hal, jadi pengetikan eksplisit dapat melewatkan kasus.

Ada diskusi yang lebih panjang tentang masalah tindak lanjut di sini: https://github.com/Microsoft/TypeScript/issues/13618 , tetapi pendekatan apa pun masih berfungsi.

Mike S.
sumber
6

Jenis pengembalian komponen fungsi terbatas JSXElement | nullpada TypeScript. Ini adalah batasan tipe saat ini, React murni memungkinkan lebih banyak tipe pengembalian.

Cuplikan demonstrasi minimal

Anda dapat menggunakan pernyataan tipe atau Fragmen sebagai solusi:

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

ReactNode

children: React.ReactNodemungkin suboptimal, jika tujuannya adalah untuk memiliki tipe yang kuat Aux.

Hampir semua hal dapat ditetapkan ke ReactNodetipe saat ini , yang setara dengan {} | undefined | null. Jenis yang lebih aman untuk casing Anda bisa jadi:

interface AuxProps {
  children: ReactElement | ReactElement[]
}

Contoh:

Mengingat Auxkebutuhan elemen React seperti children, kami secara tidak sengaja menambahkan a stringke dalamnya. Maka solusi di atas akan salah berbeda dengan ReactNode- lihatlah taman bermain yang terhubung.

Diketik childrenjuga berguna untuk props non-JSX, seperti callback Render Prop .

ford04
sumber
5

Anda juga bisa menggunakan React.PropsWithChildren<P>.

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

Cara umum untuk menemukan tipe apa pun adalah dengan contoh. Keindahan dari naskah ketikan adalah Anda memiliki akses ke semua jenis, selama Anda memiliki @types/file yang benar .

Untuk menjawab ini sendiri, saya hanya memikirkan penggunaan reaksi komponen yang memiliki childrenprop. Hal pertama yang terlintas dalam pikiran? Bagaimana dengan a <div />?

Yang perlu Anda lakukan hanyalah membuka vscode dan membuat .tsxfile baru dalam proyek react dengan @types/react.

import React from 'react';

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

Melayang di atas childrenpenyangga menunjukkan jenisnya. Dan apa yang Anda ketahui - Jenisnya adalah ReactNode(tidak perlu ReactNode[]).

masukkan deskripsi gambar di sini

Kemudian jika Anda mengklik definisi tipe, Anda akan langsung menuju definisi yang childrenberasal dari DOMAttributesantarmuka.

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

Catatan: Proses ini harus digunakan untuk menemukan jenis yang tidak diketahui! Semuanya ada di sana menunggu Anda untuk menemukannya :)

Nickofthyme
sumber
2

Sebagai tipe yang berisi anak-anak, saya menggunakan:

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

Jenis penampung anak ini cukup umum untuk mendukung semua kasus yang berbeda dan juga selaras dengan API ReactJS.

Jadi, untuk contoh Anda, itu akan menjadi seperti ini:

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

Jawaban ini tampaknya sudah usang - React sekarang memiliki tipe bawaan PropsWithChildren<{}>. Ini didefinisikan serupa dengan beberapa jawaban yang benar di halaman ini:

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

Tim Iles
sumber
1
Apa Pkasus tipe itu?
sunpietro
Padalah jenis alat peraga komponen Anda
Tim Iles
-2

Komponen React harus memiliki node pembungkus tunggal atau mengembalikan array node.

Anda <Aux>...</Aux>Komponen memiliki dua node divdan main.

Cobalah untuk membungkus anak Anda dalam divdi Auxkomponen.

import * as React from 'react';

export interface AuxProps  { 
  children: React.ReactNode
}

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

export default aux;
Dinesh Pandiyan
sumber
1
Tidak benar. Di React 16+ ini tidak lagi menjadi masalah, ditambah kesalahan akan mengatakan ini jika memang demikian
Andrew Li