Menu
Français

Coercition de types en JavaScript : implicite vs explicite

Comment JavaScript convertit les valeurs d'un type à l'autre : les règles implicites qui piègent tout le monde, les conversions explicites à privilégier, et quand chacune entre en jeu.

Les deux formes de coercition

JavaScript est plutôt souple avec les types. Quand un opérateur reçoit une valeur du « mauvais » type, le langage ne lève pas d'erreur : il convertit. Cette conversion s'appelle la coercition de type, et elle se décline en deux variantes :

  • Explicite — c'est vous qui la demandez : Number("42"), String(99), Boolean(value).
  • Implicite — un opérateur la déclenche sans que vous écriviez la moindre conversion : "5" - 2, "" == 0, if (value).

Dans les deux cas, ce sont les mêmes règles de conversion qui s'appliquent en coulisses. La seule différence, c'est de savoir qui a décidé de convertir : vous ou le langage. Et la quasi-totalité des résultats déroutants en JavaScript — "5" + 1 === "51", [] == false, null == undefined — vient d'une coercition implicite qui vous prend au dépourvu.

index.js
Output
Click Run to see the output here.

Le modèle mental à retenir : si tu écris Number(...) ou String(...), tu sais exactement ce que tu demandes. Sinon, chaque opérateur a son propre avis sur ce qu'il faut faire — et c'est précisément là que se cachent les bugs.

Les trois types cibles de la coercition

La coercition de type en JavaScript convertit toujours vers l'un des trois types primitifs suivants : string, number ou boolean. (Il existe bien un quatrième, bigint, mais aucun autre type n'est jamais converti automatiquement vers lui.) Tout le reste des règles découle simplement du type visé par l'opérateur.

index.js
Output
Click Run to see the output here.

La coercition en String est la plus permissive : chaque valeur a sa représentation textuelle. Attention cependant : les objets sont convertis en "[object Object]", ce qui n'a aucun intérêt. C'est d'ailleurs pour ça qu'afficher un objet concaténé à une chaîne ("user: " + user) ne donne quasiment jamais le résultat espéré. Privilégiez plutôt JSON.stringify ou les template literals en ciblant les champs qui vous intéressent.

Conversion vers Number en JavaScript

La coercition vers Number est beaucoup plus stricte. Pour qu'une chaîne soit convertie, elle doit réellement ressembler à un nombre, sinon vous récupérez NaN :

index.js
Output
Click Run to see the output here.

Les cas entre parenthèses, ce sont ceux qu'il faut vraiment retenir :

  • Une chaîne vide ou composée uniquement d'espaces devient 0, et non NaN.
  • null devient 0, mais undefined donne NaN.
  • Un tableau vide devient 0 ; un tableau à un seul élément coerce cet élément ; un tableau à plusieurs éléments renvoie NaN.

Pour extraire un nombre d'une chaîne qui contient d'autres caractères, utilisez plutôt parseInt ou parseFloat à la place de Number :

index.js
Output
Click Run to see the output here.

Passez toujours la base (10) à parseInt. Sans ça, les chaînes qui commencent par "0x" sont interprétées en hexadécimal, ce qui est rarement ce que vous voulez.

Conversion booléen en JavaScript

La coercition vers un booléen, c'est la plus simple des trois. Une petite liste de valeurs donne false — tout le reste donne true.

Les valeurs falsy sont :

  • false
  • 0, -0, 0n
  • "" (chaîne vide)
  • null
  • undefined
  • NaN

Et c'est tout. Toutes les autres valeurs — y compris "false", "0", [] et {} — sont truthy.

index.js
Output
Click Run to see the output here.

Les résultats avec tableau vide et objet vide piègent souvent les développeurs venus de Python, où les collections vides sont falsy. En JavaScript, c'est l'inverse : elles sont truthy. Donc si tu veux savoir « est-ce que ce tableau est vide ? », il faut le vérifier explicitement avec arr.length === 0.

La conversion booléen JavaScript se déclenche dès qu'une valeur apparaît là où un booléen est attendu : dans un if (...), un while (...), l'opérateur ternaire ? :, ou encore les opérateurs logiques &&, || et !.

L'opérateur + : un cas à part

La plupart des opérateurs arithmétiques forcent leurs opérandes à devenir des nombres. +, lui, joue sa propre partition : dès que l'un des deux côtés est une chaîne, + fait une concaténation. Sinon, il effectue une addition numérique.

index.js
Output
Click Run to see the output here.

L'évaluation se fait de la gauche vers la droite. Pour 1 + 2 + "3", on calcule d'abord 1 + 2 = 3, puis 3 + "3" = "33". En revanche, "1" + 2 + 3 démarre en mode chaîne et le reste jusqu'au bout : ça donne "12", puis "123".

C'est précisément pour ça que construire des chaînes avec + est casse-gueule. Les template literals, eux, n'ont pas ce souci :

index.js
Output
Click Run to see the output here.

Le template literal évalue count + 1 comme une expression numérique à part entière, puis interpole le résultat. Pas de coercition surprise.

Privilégier les conversions explicites

Quand tu dois convertir un type, dis-le clairement. Ça te coûte quelques caractères de plus, mais ça lève toute ambiguïté pour la personne qui lira ton code ensuite :

index.js
Output
Click Run to see the output here.

Pareil pour les booléens. !!value fonctionne et reste courant, mais Boolean(value) exprime clairement l'intention :

index.js
Output
Click Run to see the output here.

Une règle raisonnable : privilégiez les conversions explicites dans la logique métier, et gardez les formes courtes (+x, !!x) pour les endroits où la concision compte et où l'intention est évidente d'après le contexte.

L'opérateur == s'appuie lourdement sur la coercition

Les opérateurs d'égalité sont la principale source de surprises liées à la coercition en JavaScript. == applique une coercition avant de comparer ; ===, lui, n'en fait rien.

index.js
Output
Click Run to see the output here.

Chaque true ci-dessus est le résultat d'une chaîne de coercition en plusieurs étapes que la plupart des développeurs sont incapables de réciter de mémoire. Et c'est bien là le problème : du code qui marche par accident, c'est du code qui casse plus tard. On verra les règles complètes d'égalité sur la page suivante ; pour l'instant, retenez ceci : utilisez === par défaut, et ne sortez == que pour l'idiome bien précis x == null (qui attrape à la fois null et undefined).

Un exemple concret

Voici un cas pratique qui montre là où la coercition nous rend service, et là où elle nous tire dans le pied :

index.js
Output
Click Run to see the output here.

Remarquez le deuxième appel. Number("") renvoie 0, et non NaN — du coup parsePrice("") retourne 0, ce qui n'est sans doute pas ce qu'attendrait celui qui utilise cette fonction. Si on veut rejeter les entrées vides, il faut ajouter une vérification explicite :

index.js
Output
Click Run to see the output here.

Savoir quelles valeurs se convertissent en 0 plutôt qu'en NaN, c'est typiquement le genre de détail qui t'évite un bug sournois plus tard.

Ce qu'il faut retenir

  • La coercition convertit en string, number ou boolean selon l'opérateur utilisé.
  • + avec une string devient une concaténation ; tous les autres opérateurs arithmétiques forcent la conversion en number.
  • Les valeurs falsy forment une liste courte et figée — apprends-la par cœur. Tout le reste est truthy, y compris [] et {}.
  • Number("") vaut 0, Number([]) vaut 0, Number(null) vaut 0… mais Number(undefined) donne NaN. Ces subtilités causent de vrais bugs en production.
  • Privilégie Number(x), String(x), Boolean(x) plutôt que des astuces implicites un peu trop malines. Le toi du futur te remerciera.

La suite : les opérateurs d'égalité

Tout ce que fait la coercition dans les comparaisons se joue à l'intérieur de ==. La page suivante détaille == vs === vs Object.is, le seul cas où == reste vraiment utile, et pourquoi les linters signalent par défaut la forme laxiste.

Questions fréquentes

C'est quoi la coercition de types en JavaScript ?

La coercition de types, c'est JavaScript qui convertit tout seul une valeur d'un type vers un autre : un nombre qui devient chaîne, une chaîne qui devient nombre, ou n'importe quelle valeur transformée en booléen. Ça se produit de manière implicite quand un opérateur comme +, == ou un if rencontre une valeur qui n'a pas le type attendu, et de manière explicite quand c'est vous qui appelez Number(x), String(x) ou Boolean(x).

Quelle est la différence entre coercition implicite et explicite ?

La coercition explicite, c'est quand vous appelez volontairement une fonction de conversion : Number("42"), String(99), Boolean(value). L'implicite, c'est quand un opérateur déclenche la conversion dans votre dos : "5" - 2 donne 3, mais "5" + 2 donne "52". L'explicite est lisible et prévisible ; l'implicite, c'est la source de 90 % des bugs du genre « pourquoi j'obtiens NaN ? ».

Comment convertir une chaîne en nombre en JavaScript ?

Utilisez Number("42") pour une conversion stricte (renvoie NaN si la chaîne n'est pas un nombre valide), ou parseInt("42px", 10) / parseFloat("3.14em") si vous voulez extraire un nombre au début d'une chaîne mal formée. L'opérateur unaire + (+"42") fait la même chose que Number(), mais il passe facilement inaperçu quand on relit du code en diagonale.

Pourquoi [] + [] renvoie-t-il une chaîne vide ?

L'opérateur + n'a pas d'opération numérique qui ait du sens pour deux tableaux, donc JavaScript convertit les deux en chaînes. Un tableau se stringifie en joignant ses éléments par des virgules, et un tableau vide donne "". Au final, [] + [] devient "" + "", soit "". C'est amusant en soirée entre devs, et surtout une bonne raison de ne jamais se fier à + avec des valeurs non primitives.

Apprendre à coder avec Coddy

COMMENCER