TypeScript e React - tipo filho?

138

Eu tenho um componente funcional muito simples como segue:

import * as React from 'react';

export interface AuxProps  { 
    children: React.ReactNode
 }


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

export default aux;

E outro componente:

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;

Continuo recebendo o seguinte erro:

[ts] Tipo de elemento JSX 'ReactNode' não é uma função de construtor para elementos JSX. O tipo 'undefined' não pode ser atribuído ao tipo 'ElementClass'. [2605]

Como faço para digitar isso corretamente?

Asool
fonte

Respostas:

133

Somente children: React.ReactNode

Ridd
fonte
1
Esta é a resposta correta aqui! JSX.Elementnão é bom o suficiente, pois um filho React válido pode ser uma string, um booleano, nulo ... também ReactChildestá incompleto pelos mesmos motivos
Pierre Ferry
2
@PierreFerry O problema é que quase tudo pode ser atribuído ReactNode. Não ajuda em termos de segurança de tipo, semelhante a digitar childrencomo any- veja minha resposta para uma explicação.
ford04 de
Obrigado pela sua resposta @ ford04. No meu caso de uso, quero que as crianças sejam do tipo vagamente. Meu componente aceita qualquer tipo de filho React válido. Então eu acredito que esta ainda é a resposta que eu estava procurando :)
Pierre Ferry
47

Para usar <Aux>em seu JSX, ele precisa ser uma função que retorna ReactElement<any> | null. Essa é a definição de um componente de função .

No entanto, atualmente é definido como uma função que retorna React.ReactNode, que é um tipo muito mais amplo. As tipificações React dizem:

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

Certifique-se de que os tipos indesejados sejam neutralizados envolvendo o valor retornado em React Fragment ( <></>):

const aux: React.FC<AuxProps> = props =>
  <>{props.children}</>;
Karol Majewski
fonte
Não entendo por que é necessário embrulhar childrenem um fragmento de reação. Importa-se de explicar? No React é perfeitamente válido apenas return children;.
sanfilippopablo
1
Não se childrenfor uma matriz. Se for esse o caso, você precisa envolvê-los em um único nó ou usar React Fragments.
Karol Majewski
Sim, no meu código eu tenho return React.Children.count(children) > 1 ? <>{children}</> : children:, mas o texto datilografado reclama disso. Eu substituí por apenas <>{children}</>.
sanfilippopablo
2
Ele reclama porque childrené uma lista de elementos que contém apenas 1 item. Acho que funcionaria se você fizesse return React.Children.count(children) > 1 ? <>{children}</> : children[0]@sanfilippopablo
tamj0rd2
Isso não parece estar funcionando nas versões mais recentes do React. Consolo logado e posso confirmar que tenho um adereço infantil, mas mesmo com <React.Fragment>envolvente {props.children}não estou devolvendo nada.
Marcos
34

Isto é o que funcionou para mim:

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

Editar Eu recomendaria usar children: React.ReactNodeagora.

Sunknudsen
fonte
8
Essa não é uma definição ampla o suficiente. A criança pode ser uma corda.
Mike S
Pelo menos este exemplo funcionou para mim, já que meu componente deveria esperar 1 ou mais JSX.Elements. Obrigado!
Italo Borges
1
Isso não funcionará com expressões JavaScript como filhos <MyComponent>{ condition ? <span>test</span> : null}</MyComponent>. Usar children: ReactNodevai funcionar.
Nickofthyme
10

você pode declarar seu componente assim:

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

Você pode usar ReactChildrene ReactChild:

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

interface AuxProps {
  children: ReactChild | ReactChildren;
}

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

export default Aux;
Wilk
fonte
6

Do site TypeScript: https://github.com/Microsoft/TypeScript/issues/6471

A prática recomendada é escrever o tipo de adereços como {crianças ?: qualquer}

Isso funcionou para mim. O nó filho pode ser muitas coisas diferentes, então a digitação explícita pode perder casos.

Há uma discussão mais longa sobre o problema de acompanhamento aqui: https://github.com/Microsoft/TypeScript/issues/13618 , mas qualquer abordagem ainda funciona.

Mike S
fonte
6

O tipo de retorno do componente de função é limitado JSXElement | nullem TypeScript. Esta é uma limitação de tipo atual, o React puro permite mais tipos de retorno.

Trecho mínimo de demonstração

Você pode usar uma declaração de tipo ou fragmentos como solução alternativa:

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

ReactNode

children: React.ReactNodepode ser inferior ao ideal, se o objetivo for ter tipos fortes para Aux.

Quase tudo pode ser atribuído ao ReactNodetipo atual , que é equivalente a {} | undefined | null. Um tipo mais seguro para o seu caso poderia ser:

interface AuxProps {
  children: ReactElement | ReactElement[]
}

Exemplo:

Dadas Auxnecessidades React elementos como children, acidentalmente adicionamos a stringa ele. Então, a solução acima seria um erro em contraste com ReactNode- dê uma olhada nos playgrounds vinculados.

Digitados childrentambém são úteis para adereços não JSX, como um retorno de chamada Render Prop .

ford04
fonte
5

Você também pode usar React.PropsWithChildren<P>.

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

A maneira geral de encontrar qualquer tipo é por exemplo. A beleza do texto datilografado é que você tem acesso a todos os tipos, desde que tenha os @types/arquivos corretos .

Para responder a isso eu mesmo pensei em um componente reage usa que tem o childrenprop. A primeira coisa que me veio à cabeça? Que tal um <div />?

Tudo que você precisa fazer é abrir o vscode e criar um novo .tsxarquivo em um projeto react com @types/react.

import React from 'react';

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

Passar o mouse sobre o childrenadereço mostra o tipo. E o que você sabe - Seu tipo é ReactNode(não há necessidade de ReactNode[]).

insira a descrição da imagem aqui

Então, se você clicar na definição de tipo, será direcionado diretamente para a definição de childrenvir da DOMAttributesinterface.

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

Nota: Este processo deve ser usado para encontrar qualquer tipo desconhecido! Todos eles estão lá apenas esperando que você os encontre :)

Nickofthyme
fonte
2

Como um tipo que contém filhos, estou usando:

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

Este tipo de contêiner filho é genérico o suficiente para suportar todos os casos diferentes e também está alinhado com a API ReactJS.

Então, para o seu exemplo, seria algo como:

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

Essas respostas parecem estar desatualizadas - o React agora tem um tipo integrado PropsWithChildren<{}>. É definido de forma semelhante a algumas das respostas corretas nesta página:

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

Tim Iles
fonte
1
Qual é Po caso desse tipo?
sunpietro
Pé o tipo de adereços do seu componente
Tim Iles
-2

Os componentes React devem ter um único nó wrapper ou retornar uma matriz de nós.

Seu <Aux>...</Aux>componente possui dois nós dive main.

Tente envolver seus filhos em um divno Auxcomponente.

import * as React from 'react';

export interface AuxProps  { 
  children: React.ReactNode
}

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

export default aux;
Dinesh Pandiyan
fonte
1
Não é verdade. No React 16+, esse não é mais o caso, e o erro diria isso se fosse o caso
Andrew Li