¿Qué se esconde detrás del prop children en React y TypeScript?
\n\nSi alguna vez has escrito un componente en React usando TypeScript, seguramente te has enfrentado a la pregunta: "¿Qué tipo de datos tiene el prop children?". Esta pregunta es una de las más populares en Stack Overflow, y no es para menos. A primera vista, parece que la respuesta es simple: ReactNode. Pero, como se dice, el diablo está en los detalles.
En este artículo no solo daremos una respuesta seca. Analizaremos todos los tipos posibles para children, sus diferencias, mejores prácticas y trampas. Aprenderás cuándo usar ReactNode, cuándo ReactElement y cuándo tipos más específicos. ¡Allá vamos!
¿Por qué esta pregunta es tan popular?
\n\nEl problema es que children es un prop especial en React. Puede ser cualquier cosa:
- \n
- Una cadena:
"¡Hola, mundo!"\n - Un número:
{42}\n - Un elemento JSX:
<div>...</div>\n - Un array de elementos:
[<span/>, <span/>]\n - Una función (render prop):
{() => <div/>}\n - Incluso
null,undefinedoboolean\n
Debido a tanta flexibilidad, los principiantes suelen escribir children: any, lo que anula todo el beneficio de TypeScript. Analicemos los enfoques correctos.
Tipos principales para children
\n\n1. ReactNode — el todoterreno
\n\nEste es el tipo más común y correcto para children en el 90% de los casos. Incluye todo lo que React puede 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="Mi tarjeta">\n <p>Este texto será renderizado</p>\n {123} {/* Los números también funcionan */}\n {null} {/* Y null */}\n</Card>\n\n¿Qué incluye ReactNode? Básicamente es la unión de:
\n- \n
ReactElement— elementos JSX \n stringynumber— primitivos \n boolean,null,undefined— se ignoran al renderizar \n ReactNode[]— array de lo anterior \n ReactPortal— portales \n
2. ReactElement — modo estricto
\n\nSi necesitas que children sea solo un elemento JSX (no una cadena ni un número), usa ReactElement.
import { ReactElement } from 'react';\n\ninterface StrictCardProps {\n children: ReactElement; // Solo un elemento JSX\n}\n\nfunction StrictCard({ children }: StrictCardProps) {\n return <div className="strict-card">{children}</div>;\n}\n\n// Funciona:\n<StrictCard><span>Solo elemento</span></StrictCard>\n\n// Error de TypeScript:\n<StrictCard>Texto</StrictCard> // ❌ La cadena no es válida\n\nImportante: ReactElement requiere exactamente un elemento hijo. Si pasas dos elementos sin envolverlos, TypeScript también dará error.
3. JSX.Element — alias de ReactElement
\n\nEl tipo JSX.Element es simplemente ReactElement<any, any>. Es menos flexible que ReactNode y generalmente no se recomienda para children, a menos que estés escribiendo una biblioteca.
// Básicamente, es lo mismo:\ntype A = JSX.Element; // ReactElement<any, any>\ntype B = ReactElement; // Re