O que está por trás da prop children em React e TypeScript?
\n\nSe você já escreveu um componente React usando TypeScript, certamente já se deparou com a pergunta: "Qual é o tipo de dado da prop children?". Esta pergunta é uma das mais populares no Stack Overflow, e não é à toa. À primeira vista, parece que a resposta é simples: ReactNode. Mas o diabo, como dizem, está nos detalhes.
Neste artigo, não vamos apenas dar uma resposta seca. Vamos analisar todos os tipos possíveis para children, suas diferenças, melhores práticas e armadilhas. Você aprenderá quando usar ReactNode, quando usar ReactElement e quando usar tipos mais específicos. Vamos lá!
Por que esta pergunta é tão popular?
\n\nO problema é que children é uma prop especial no React. Ela pode ser qualquer coisa:
- \n
- String:
"Olá, mundo!"\n - Número:
{42}\n - Elemento JSX:
<div>...</div>\n - Array de elementos:
[<span/>, <span/>]\n - Função (render prop):
{() => <div/>}\n - Até mesmo
null,undefinedouboolean\n
Devido a essa flexibilidade, iniciantes frequentemente escrevem children: any, o que anula todos os benefícios do TypeScript. Vamos entender as abordagens corretas.
Tipos principais para children
\n\n1. ReactNode — o coringa universal
\n\nEste é o tipo mais comum e correto para children em 90% dos casos. Ele inclui tudo que o React pode renderizar.
import { ReactNode } from 'react';\n\ninterface CardProps {\n title: string;\n children: ReactNode;\n}\n\nfunction Card({ title, children }: CardProps) {\n return (\n <div className="card">\n <h2>{title}</h2>\n <div className="card-content">\n {children}\n </div>\n </div>\n );\n}\n\n// Uso:\n<Card title="Meu cartão">\n <p>Este texto será renderizado</p>\n {123} {/* Números também funcionam */}\n {null} {/* E null */}\n</Card>\n\nO que está incluído em ReactNode? Na prática, é a união de:
\n- \n
ReactElement— elementos JSX \n stringenumber— primitivos \n boolean,null,undefined— ignorados na renderização \n ReactNode[]— array dos itens acima \n ReactPortal— portais \n
2. ReactElement — modo estrito
\n\nSe você precisa que children seja apenas um elemento JSX (não string ou número), use ReactElement.
import { ReactElement } from 'react';\n\ninterface StrictCardProps {\n children: ReactElement; // Apenas um elemento JSX\n}\n\nfunction StrictCard({ children }: StrictCardProps) {\n return <div className="strict-card">{children}</div>;\n}\n\n// Funciona:\n<StrictCard><span>Apenas elemento</span></StrictCard>\n\n// Erro do TypeScript:\n<StrictCard>Texto</StrictCard> // ❌ String não é aceita\n\nImportante: ReactElement exige exatamente um elemento filho. Se você passar dois elementos sem um wrapper, o TypeScript também emitirá um erro.
3. JSX.Element — alias para ReactElement
\n\nO tipo JSX.Element é simplesmente ReactElement<any, any>. Ele é menos flexível que ReactNode e geralmente não é recomendado para children, a menos que você esteja escrevendo uma biblioteca.
// Na prática, é a mesma coisa:\ntype A = JSX.Element; // ReactElement<any, any>\ntype B = ReactElement; // Re