« La propriété value n'existe pas » : comment faire collaborer TypeScript et le DOM
\n\nAvez-vous déjà écrit un simple gestionnaire de formulaire en TypeScript, lancé la compilation — et obtenu dans la console une ligne rouge : « The property 'value' does not exist on value of type 'HTMLElement' » ? Si oui, vous n'êtes pas seul. Cette question a rassemblé des milliers de vues sur Stack Overflow, et aujourd'hui nous allons l'analyser une fois pour toutes.
\n\nL'erreur se produit lorsque vous essayez d'obtenir .value d'un élément que TypeScript considère simplement comme HTMLElement. Le problème est que HTMLElement est un type de base, alors que la propriété value n'existe que sur HTMLInputElement, HTMLTextAreaElement et leurs descendants similaires. TypeScript est strict : il ne vous permettra pas d'accéder à une propriété qui n'existe pas (de son point de vue).
Mais ce n'est pas un bug — c'est une fonctionnalité. TypeScript vous protège des erreurs d'exécution. Voyons comment spécifier correctement le type et, par la même occasion, améliorer les performances de votre code.
\n\nPourquoi TypeScript se plaint-il ?
\n\nImaginez : vous écrivez du code pour un champ de saisie :
\n\nconst input = document.getElementById('myInput');\nconsole.log(input.value); // ❌ Erreur !\n\nLa méthode getElementById retourne le type HTMLElement | null. HTMLElement n'a pas de propriété value. Il a innerHTML, className, style — mais pas value. TypeScript dit honnêtement : « Je ne suis pas sûr que cet élément ait une propriété value ».
Pourtant, vous êtes sûr qu'il s'agit d'un <input>. Comment l'expliquer au compilateur ?
Méthode 1 : Cast de type (Type Assertion)
\n\nLa méthode la plus populaire et la plus rapide consiste à utiliser as :
const input = document.getElementById('myInput') as HTMLInputElement;\nconsole.log(input.value); // ✅ Tout fonctionne\n\nMaintenant, TypeScript sait : c'est un HTMLInputElement, qui a une propriété value. Simple et efficace. Mais il y a un détail : si l'élément n'est pas réellement un <input> (par exemple, c'est un <div>), vous obtiendrez undefined à l'exécution. TypeScript ne vous aidera pas — il croit vos affirmations.
Méthode 2 : Vérification de type avec instanceof
\n\nUne approche plus sûre consiste à vérifier lors de l'exécution :
\n\nconst element = document.getElementById('myInput');\nif (element instanceof HTMLInputElement) {\n console.log(element.value); // ✅ TypeScript réduit automatiquement le type\n} else {\n console.error('L\'élément n\'est pas un input');\n}\n\nCette méthode non seulement résout le problème de typage, mais rend également le code plus fiable. Vous vérifiez explicitement que l'élément est bien celui attendu. À des fins pédagogiques, je recommande cette méthode : elle apprend à réfléchir aux types.
\n\nMéthode 3 : Fonctions génériques (Generics)
\n\nPour les utilisateurs avancés — création d'une enveloppe avec des génériques :
\n\nfunction getInputElement<T extends HTMLElement>(id: string): T | null {\n const element = document.getElementById(id);\n if (element instanceof T) { // ❌ Impossible, T est un type, pas une valeur\n return element;\n }\n return null;\n}\n\nMalheureusement, en TypeScript, on ne peut pas vérifier instanceof directement avec un générique. Mais on peut utiliser des surcharges de fonctions ou des fabriques. Pour les projets simples, les deux premières méthodes suffisent.
Optimisation des performances : pourquoi le type est important
\n\nVous pourriez demander : « Quel rapport les types ont-ils avec la vitesse ? » Direct. Lorsque TypeScript connaît le type exact d'un élément, il génère un code JavaScript plus efficace. Prenons un exemple :
\n\n// Mauvais : TypeScript ne connaît pas le type\nfunction getValue(id: string): string {\n const el = document.getElementById(id);\n // À chaque appel — vérification et cast\n return (el as HTMLInputElement).value || '';\n}\n\n// Bo