TypeScript та React - тип дітей?


138

У мене дуже простий функціональний компонент:

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. Тип 'undefined' не призначається типу 'ElementClass'. [2605]

Як правильно ввести це?

Відповіді:


133

Просто children: React.ReactNode


1
Тут правильна відповідь! JSX.Elementнедостатньо добре, оскільки дійсні діти, що реагують, можуть бути струнними, булевими, нульовими ... ReactChildтеж неповні з тих же причин
П'єр Феррі

2
@PierreFerry Проблема полягає в тому, що майже все може бути призначено ReactNode. Це не допоможе , з точки зору безпеки типу, подібно типізації , childrenяк any- дивіться мій відповідь для пояснення.
ford04

Дякуємо за вашу відповідь @ ford04. У моєму випадку використання я хочу, щоб діти були неміцними. Мій компонент приймає будь-які дійсні діти React. Тому я вважаю, що це все ще відповідь, яку я шукав :)
П'єр Феррі

47

Для використання <Aux>у вашому JSX це повинна бути функція, яка повертається 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 це абсолютно справедливо return children;.
sanfilippopablo

1
Не якщо childrenце масив. Якщо це так, вам потрібно або обернути їх в один вузол, або використовувати Реагувати фрагменти.
Кароль Маєвський

Так, у своєму коді я маю:, return React.Children.count(children) > 1 ? <>{children}</> : childrenале машинопис скаржиться на це. Я замінив його просто <>{children}</>.
sanfilippopablo

2
Він скаржиться, тому що childrenце список елементів, який, як правило, містить лише 1 елемент. Я думаю, це спрацювало б, якщо ти зробив return React.Children.count(children) > 1 ? <>{children}</> : children[0]@sanfilippopablo
tamj0rd2

Схоже, це не працює в нових версіях React. Я консолі ввійшов і можу підтвердити, що я маю дітей, але навіть із <React.Fragment>оточуючими {props.children}нічого не повертаю.
Марк

34

Це для мене працювало:

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

Редагувати, я б рекомендував children: React.ReactNodeзамість цього використовувати зараз.


8
Це недостатньо широке визначення. Дитина може бути струною.
Майк S

Принаймні цей приклад працював для мене, оскільки мій компонент повинен був очікувати 1 або більше JSX.Elements. Дякую!
Італо Борхес

1
Це не працюватиме з виразами JavaScript як діти <MyComponent>{ condition ? <span>test</span> : null}</MyComponent>. Використання children: ReactNodeбуде працювати.
Nickofthyme

10

ви можете оголосити свій компонент так:

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

9

Ви можете використовувати ReactChildrenта ReactChild:

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

interface AuxProps {
  children: ReactChild | ReactChildren;
}

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

export default Aux;

6

З сайту TypeScript: https://github.com/Microsoft/TypeScript/isissue/6471

Рекомендована практика - писати тип реквізиту як {children ?: any}

Це працювало для мене. У дочірньому вузлі може бути багато різних речей, тому чітке введення тексту може пропускати випадки.

Продовження дискусії щодо подальшої проблеми тут: https://github.com/Microsoft/TypeScript/isissue/13618 , але будь-який підхід все ще працює.


6

Тип повернення функціонального компонента обмежений JSXElement | nullу TypeScript. Це обмеження поточного типу, чистий React дозволяє отримати більше типів повернення.

Мінімальний демонстраційний фрагмент

Ви можете використовувати твердження типу або фрагменти як вирішення:

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

ReactNode

children: React.ReactNodeможе бути неоптимальним, якщо метою є наявність сильних типів для Aux.

Поточному типу може бути присвоєно майже все ReactNode, що еквівалентно {} | undefined | null. Більш безпечним для вашого випадку може бути:

interface AuxProps {
  children: ReactElement | ReactElement[]
}

Приклад:

Враховуючи Auxпотреби в елементах React children, ми випадково додали stringїх. Тоді вище рішення буде помилкою на відміну від ReactNode- подивіться на пов’язані майданчики.

Введені childrenтакож корисні для реквізитів, що не є JSX, як зворотний виклик Render Prop .


5

Ви також можете використовувати React.PropsWithChildren<P>.

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

3

Загальний спосіб знайти будь-який тип на прикладі. Краса машинопису полягає в тому, що у вас є доступ до всіх типів, якщо у вас є правильні @types/файли.

Щоб відповісти на це сам, я просто подумав про використання компонентів, які реагують на childrenпідтримку. Перше, що прийшло в голову? Як щодо <div />?

Все, що вам потрібно зробити - це відкрити vscode та створити новий .tsxфайл у реагуванні проекту @types/react.

import React from 'react';

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

Наведення курсор на childrenопору показує тип. І що ти знаєш - Його типReactNode (не потрібно ReactNode[]).

введіть тут опис зображення

Тоді, якщо ви натиснете на визначення типу, це приведе вас прямо до визначення children надходить з DOMAttributesінтерфейсу.

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

Примітка. Цей процес слід використовувати для пошуку будь-якого невідомого типу! Усі вони там просто чекають, коли ви їх знайдете :)


2

Як тип, який містить дітей, я використовую:

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

Цей дитячий тип контейнера достатньо загальний для підтримки всіх різних випадків, а також узгоджується з API ReactJS.

Отже, для вашого прикладу це було б щось на зразок:

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

1

Ці відповіді видаються застарілими - React тепер має вбудований тип PropsWithChildren<{}>. Це визначено подібно до деяких правильних відповідей на цій сторінці:

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


1
Що Pстосується такого типу?
sunpietro

P- тип реквізиту вашого компонента
Тім Ілз

-2

Компоненти реагування повинні мати один вузол обгортки або повертати масив вузлів.

Ваш <Aux>...</Aux>компонент має два вузли divтаmain .

Спробуйте загорнути своїх дітей divу Auxскладові.

import * as React from 'react';

export interface AuxProps  { 
  children: React.ReactNode
}

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

export default aux;

1
Неправда. У React 16+ це вже не так, плюс помилка сказала б це, якби це було так
Ендрю Лі
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.