« Property 'value' does not exist on type 'EventTarget' » — comment arrêter d'avoir peur et commencer à typer les événements en TypeScript
\n\nAvez-vous déjà écrit un gestionnaire d'événements en TypeScript, essayé d'obtenir event.target.value — et reçu immédiatement un soulignement rouge et le message : « Property 'value' does not exist on type 'EventTarget' » ? Si oui, vous n'êtes pas seul. Sur Stack Overflow, cette question a recueilli des dizaines de milliers de vues, et elle continue de tourmenter les débutants et les développeurs expérimentés en 2025.
Le problème n'est pas que TypeScript est « méchant » ou « cassé ». C'est tout le contraire : le compilateur vous protège contre des erreurs stupides qui pourraient survenir à l'exécution. Comprenons pourquoi cette erreur se produit et comment la corriger correctement.
\n\nPourquoi TypeScript se plaint-il de event.target.value ?
\n\nRéponse courte : event.target a le type EventTarget | null. Et EventTarget n'a pas de propriété value. Il n'a que trois méthodes : addEventListener, removeEventListener et dispatchEvent.
Lorsque vous écrivez :
\n\nconst handleChange = (event: React.ChangeEvent) => {\n const value = event.target.value; // ❌ Erreur !\n};\n\n\nTypeScript voit : « Hé, je ne sais pas quel élément HTML spécifique a déclenché cet événement. Peut-être que c'est un <div>, qui n'a pas de value, ou peut-être un <input> — je n'en suis pas sûr. Donc je refuse de compiler ce code ».
C'est une fonctionnalité, pas un bug. TypeScript vous oblige à spécifier explicitement avec quel élément vous travaillez.
\n\nTop 3 des méthodes de résolution (à jour pour 2025)
\n\nIl existe plusieurs approches. Nous allons examiner les plus populaires et les plus sûres.
\n\n1. Cast avec as HTMLInputElement (rapide, mais prudent)
\n\nLa méthode la plus courante — convertir event.target au type souhaité :
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n const value = (event.target as HTMLInputElement).value;\n console.log(value);\n};\n\n\nAvantages : simple, clair, fonctionne.
\nInconvénients : si vous vous trompez et que l'événement provient en réalité d'un <textarea> ou d'un <select>, vous obtiendrez undefined à l'exécution. Le compilateur ne vous préviendra pas.
Il est préférable d'utiliser cette méthode lorsque vous êtes absolument certain du type de l'élément. Par exemple, dans des formulaires simples avec un seul champ.
\n\n2. Utiliser event.currentTarget au lieu de event.target
\n\nEn TypeScript, currentTarget est typé plus strictement que target. Si vous spécifiez un paramètre générique pour l'événement, alors currentTarget aura le type correct :
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {\n const value = event.currentTarget.value; // ✅ Fonctionne sans cast !\n};\n\n\nImportant : currentTarget est l'élément sur lequel le gestionnaire est abonné. Si vous vous êtes abonné à un <input>, alors currentTarget sera toujours ce <input>. Contrairement à target, qui peut être un élément imbriqué (par exemple, un <span> à l'intérieur d'un bouton).
Avantages : plus sûr qu'un cast, pas besoin d'écrire de conversions supplémentaires.
\nInconvénients : ne convient pas à tous les scénarios — par exemple, pour les événements de remontée (bubbling), currentTarget peut ne pas correspondre à l'élément cible.
3. Fonction générique avec vérification (la méthode la plus sûre)
\n\nSi vous voulez une sécurité maximale et une réutilisabilité, écrivez une fonction utilitaire qui vérifie le type de l'élément :
\n\nfunction getInputValue(event: Event): string