Qu'est-ce qui se cache derrière la prop children dans React et TypeScript ?
\n\nSi vous avez déjà écrit un composant React avec TypeScript, vous avez probablement été confronté à la question : "Quel est le type de données de la prop children ?". Cette question est l'une des plus populaires sur Stack Overflow, et pour cause. À première vue, la réponse semble simple : ReactNode. Mais le diable, comme on dit, se cache dans les détails.
Dans cet article, nous ne donnerons pas simplement une réponse sèche. Nous analyserons tous les types possibles pour children, leurs différences, les bonnes pratiques et les pièges à éviter. Vous apprendrez quand utiliser ReactNode, quand utiliser ReactElement, et quand utiliser des types plus spécifiques. C'est parti !
Pourquoi cette question est-elle si populaire ?
\n\nLe problème est que children est une prop spéciale dans React. Elle peut être n'importe quoi :
- \n
- Une chaîne de caractères :
"Bonjour, le monde !"\n - Un nombre :
{42}\n - Un élément JSX :
<div>...</div>\n - Un tableau d'éléments :
[<span/>, <span/>]\n - Une fonction (render prop) :
{() => <div/>}\n - Même
null,undefinedouboolean\n
En raison d'une telle flexibilité, les débutants écrivent souvent children: any, ce qui annule tous les avantages de TypeScript. Analysons les bonnes approches.
Les principaux types pour children
\n\n1. ReactNode — le soldat universel
\n\nC'est le type le plus courant et le plus correct pour children dans 90% des cas. Il inclut tout ce que React peut afficher.
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// Utilisation :\n<Card title="Ma carte">\n <p>Ce texte sera affiché</p>\n {123} {/* Les nombres fonctionnent aussi */}\n {null} {/* Et null */}\n</Card>\n\nQu'est-ce qui est inclus dans ReactNode ? En fait, c'est une union de :
\n- \n
ReactElement— les éléments JSX \n stringetnumber— les primitives \n boolean,null,undefined— ignorés lors du rendu \n ReactNode[]— un tableau de ce qui précède \n ReactPortal— les portails \n
2. ReactElement — le mode strict
\n\nSi vous avez besoin que children soit uniquement un élément JSX (pas une chaîne ni un nombre), utilisez ReactElement.
import { ReactElement } from 'react';\n\ninterface StrictCardProps {\n children: ReactElement; // Un seul élément JSX\n}\n\nfunction StrictCard({ children }: StrictCardProps) {\n return <div className="strict-card">{children}</div>;\n}\n\n// Fonctionne :\n<StrictCard><span>Uniquement un élément</span></StrictCard>\n\n// Erreur TypeScript :\n<StrictCard>Texte</StrictCard> // ❌ La chaîne ne convient pas\n\nImportant : ReactElement exige exactement un seul élément enfant. Si vous passez deux éléments sans les envelopper, TypeScript générera également une erreur.
3. JSX.Element — un alias pour ReactElement
\n\nLe type JSX.Element est simplement ReactElement<any, any>. Il est moins flexible que ReactNode et n'est généralement pas recommandé pour children, sauf si vous écrivez une bibliothèque.
// En substance, c'est la même chose :\ntype A = JSX.Element; // ReactElement<any, any>\ntype B = ReactElement; // Re