Développement Web

Valeurs, types et coercition en JavaScript moderne

13 دقائق للقراءة

📍 Le guide du parcours : JavaScript moderne : le guide complet

Ce tutoriel ouvre la série consacrée au JavaScript moderne. Pour la vue d’ensemble et l’ordre conseillé, lisez d’abord le guide.

Introduction

Vous tapez "3" + 1 dans la console et vous obtenez "31". Vous tapez "3" - 1 et vous obtenez 2. Le même opérande, deux comportements opposés : bienvenue dans le monde des types et de la coercition en JavaScript. Loin d’être des bizarreries à craindre, ces règles sont parfaitement logiques une fois qu’on a compris les sept types primitifs du langage et la manière dont il convertit les valeurs. C’est précisément ce que vous saurez faire à la fin de ce tutoriel : prédire le type d’une valeur, savoir quand JavaScript la convertit, et écrire des comparaisons qui ne réservent aucune surprise.

🎯 Ce que vous allez apprendre

  • Distinguer les sept types primitifs et le type objet, et reconnaître chacun avec typeof.
  • Comprendre la différence entre null et undefined, et pourquoi typeof null vaut "object".
  • Anticiper la coercition implicite et choisir entre === et == en connaissance de cause.
  • Manipuler correctement les nombres, les chaînes et les booléens, y compris les cas limites comme NaN.
  • Valider les champs d’un objet en testant leur type, sur un cas réel.

🛠️ Ce que vous allez construire

Vous allez modéliser la donnée centrale de l’application CarnetTâches : l’objet tâche. Au passage, vous écrirez une petite fonction decrireType() qui révèle le vrai type de n’importe quelle valeur, puis un validateur qui refuse une tâche mal formée. Le résultat est une base de données en mémoire propre, sur laquelle tous les tutoriels suivants s’appuieront.

Prérequis

  • Un navigateur récent (Chrome, Firefox, Edge) et sa console de développement (touche F12, onglet « Console »).
  • Aucune installation : tout le code de ce tutoriel s’exécute directement dans la console.
  • Niveau débutant. Test express : si vous savez ouvrir la console et y taper 1 + 1, vous êtes prêt.
  • ⏱️ Temps estimé : ~35 minutes.

Étape 1 — Reconnaître les types primitifs

Avant de modéliser quoi que ce soit, il faut savoir avec quelles briques on travaille. JavaScript définit huit types : sept primitifs et le type objet. Les primitifs représentent des valeurs simples et immuables. Les voici, avec une valeur d’exemple pour chacun. L’opérateur typeof renvoie une chaîne décrivant le type d’une valeur ; c’est notre premier outil d’inspection.

typeof undefined;        // "undefined" — pas de valeur assignée
typeof null;             // "object"   — bug historique, voir plus bas
typeof true;             // "boolean"  — vrai ou faux
typeof 42;               // "number"   — tous les nombres (entiers et décimaux)
typeof 9007199254740993n;// "bigint"   — entiers de précision arbitraire
typeof "tâche";          // "string"   — texte
typeof Symbol("id");     // "symbol"   — identifiant unique
typeof { titre: "x" };   // "object"   — objets, tableaux, dates...
typeof function () {};    // "function" — cas particulier, c'est un objet appelable

Notez deux pièges immédiats. D’abord, typeof null ne renvoie pas "null" mais "object" : c’est une erreur des tout premiers jours du langage, conservée pour ne pas casser le code existant. Pour tester une valeur nulle, comparez-la directement : valeur === null. Ensuite, typeof sur une fonction renvoie "function" alors qu’une fonction est techniquement un objet — une commodité bien utile pour savoir si une valeur est appelable.

Point d’étape — Ouvrez votre console et tapez chacune de ces lignes. Vous devez voir exactement les chaînes commentées à droite. Si typeof null vous renvoie autre chose que "object", c’est que vous avez fait une faute de frappe.

Étape 2 — Comprendre null et undefined

Ces deux valeurs expriment l’absence, mais pas de la même façon, et confondre les deux est une source classique de bugs. undefined signifie « cette variable existe mais n’a jamais reçu de valeur » — c’est ce que JavaScript met par défaut. null signifie « j’ai volontairement mis une absence ici » — c’est une décision du développeur. La distinction compte au moment de modéliser une tâche dont l’échéance n’est pas encore fixée.

let echeance;            // déclarée, pas initialisée
console.log(echeance);   // undefined

echeance = null;         // on affirme : « pas d'échéance pour l'instant »
console.log(echeance);   // null

// Conséquence sur les comparaisons :
null == undefined;       // true  — l'égalité permissive les confond
null === undefined;      // false — l'égalité stricte les distingue
typeof undefined;        // "undefined"
typeof null;             // "object"

En pratique, choisissez une convention et tenez-vous-y. Pour CarnetTâches, une échéance absente sera représentée par null : c’est une absence assumée, pas un oubli. Tester tache.echeance === null est alors sans ambiguïté.

Étape 3 — Les nombres et le cas de NaN

JavaScript n’a qu’un seul type pour les entiers et les décimaux : number, un flottant 64 bits. Cela simplifie l’écriture mais impose de connaître ses limites. La plus connue est NaN (Not a Number), la valeur renvoyée quand un calcul numérique échoue, par exemple en multipliant un texte non numérique.

Number("12");            // 12   — conversion réussie
Number("douze");         // NaN  — échec de conversion
0.1 + 0.2;               // 0.30000000000000004 — arrondi des flottants

// NaN a une propriété déroutante : il n'est égal à rien, pas même à lui-même
NaN === NaN;             // false
Number.isNaN(NaN);       // true  — la bonne façon de tester
Number.isNaN("texte");   // false — ne convertit pas, contrairement au global isNaN()

// Bornes de sécurité des entiers
Number.MAX_SAFE_INTEGER; // 9007199254740991

Deux réflexes à retenir. Pour tester si une valeur est NaN, utilisez Number.isNaN() et jamais une comparaison directe, puisque NaN n’est égal à rien. Et pour des entiers dépassant Number.MAX_SAFE_INTEGER, basculez sur le type bigint en suffixant le nombre d’un n : 9007199254740993n. Pour les priorités de nos tâches (1, 2 ou 3), le type number classique suffit amplement.

Point d’étape — Tapez 0.1 + 0.2 === 0.3 dans la console. Vous devez obtenir false. C’est normal : les flottants ne représentent pas exactement certains décimaux. Pour comparer des montants, on travaille en plus petites unités entières ou on arrondit explicitement.

Étape 4 — La coercition, ou la conversion automatique

Le cœur des surprises de JavaScript se trouve ici. Comme le langage est à typage dynamique, il convertit volontiers une valeur d’un type vers un autre quand le contexte l’exige. On parle de coercition. Comprendre ses règles, c’est passer de « JavaScript fait n’importe quoi » à « JavaScript suit des règles que je connais ».

L’opérateur + est le plus piégeux car il sert à la fois à additionner des nombres et à concaténer des chaînes. Dès qu’un de ses opérandes est une chaîne, il bascule en mode concaténation. Les autres opérateurs arithmétiques (-, *, /) n’ont pas cette ambiguïté : ils convertissent toujours vers number.

"3" + 1;                 // "31"  — concaténation : 1 devient "1"
"3" - 1;                 // 2     — soustraction : "3" devient 3
true + 1;                // 2     — true vaut 1 en numérique
[] + [];                 // ""    — deux tableaux convertis en chaînes vides
"5" * "2";               // 10    — les deux chaînes deviennent des nombres

// Coercition vers booléen dans un contexte conditionnel
Boolean("");             // false — chaîne vide
Boolean("0");            // true  — chaîne non vide, attention !
Boolean(0);              // false
Boolean(null);           // false

Retenez la liste des valeurs « fausses » (falsy) : false, 0, -0, 0n, "", null, undefined et NaN. Tout le reste est « vrai » (truthy), y compris la chaîne "0" et le tableau vide []. C’est pourquoi if (tache.titre) est un test pratique pour « le titre n’est pas vide », à condition de se souvenir que "0" y serait considéré comme rempli.

Étape 5 — Égalité stricte contre égalité permissive

Vous disposez maintenant de tout pour comprendre la règle d’or des comparaisons. L’égalité permissive == applique la coercition avant de comparer, ce qui produit des résultats que personne ne devine de tête. L’égalité stricte === ne convertit rien : elle exige même type et même valeur. Dans la quasi-totalité des cas, c’est === qu’il faut utiliser.

0 == "";                 // true  — coercition vers nombre
0 == "0";                // true
"" == "0";               // false — déroutant : == n'est pas transitif !
false == "false";        // false — "false" devient NaN
0 === "";                // false — types différents, pas de conversion
1 === 1;                 // true

// Le cas particulier que === ne gère pas : NaN et -0
NaN === NaN;             // false
Object.is(NaN, NaN);     // true  — Object.is distingue ces cas limites
Object.is(-0, 0);        // false

L’exemple 0 == "" qui vaut true alors que "" == "0" vaut false montre que l’égalité permissive n’est même pas transitive. C’est ingérable mentalement. Adoptez === comme réflexe ; la seule exception courante et défendable est valeur == null, qui teste d’un coup null et undefined — pratique, mais à utiliser en connaissance de cause. Pour les cas limites NaN et -0, Object.is() donne le résultat « intuitif » que === ne fournit pas.

Point d’étape — Prédisez le résultat de "" == "0" avant de l’exécuter, puis vérifiez. Si vous aviez anticipé false en expliquant pourquoi, vous avez compris la coercition.

Étape 6 — Modéliser et valider une tâche

Assemblons tout. Une tâche de CarnetTâches est un objet, et chacun de ses champs a un type attendu. On va écrire un validateur qui vérifie ces types et rejette toute tâche mal formée — exactement le genre de garde-fou qui évite des bugs en cascade plus tard.

function decrireType(valeur) {
  if (valeur === null) return "null";
  if (Array.isArray(valeur)) return "array";   // typeof [] vaut "object", on précise
  return typeof valeur;
}

function validerTache(t) {
  const erreurs = [];
  if (decrireType(t.titre) !== "string" || t.titre.trim() === "")
    erreurs.push("titre manquant");
  if (decrireType(t.fait) !== "boolean")
    erreurs.push("le champ fait doit être un booléen");
  if (decrireType(t.priorite) !== "number" || ![1, 2, 3].includes(t.priorite))
    erreurs.push("priorité invalide (1, 2 ou 3)");
  if (decrireType(t.etiquettes) !== "array")
    erreurs.push("etiquettes doit être un tableau");
  // echeance : soit une chaîne ISO, soit null (absence assumée)
  if (t.echeance !== null && decrireType(t.echeance) !== "string")
    erreurs.push("echeance doit être une chaîne ou null");
  return erreurs;
}

const bonne = { titre: "Réviser", fait: false, priorite: 2, etiquettes: ["étude"], echeance: null };
const mauvaise = { titre: "", fait: "oui", priorite: 9, etiquettes: "étude", echeance: 12345 };

console.log(validerTache(bonne));   // []  → valide
console.log(validerTache(mauvaise));// ["titre manquant", "le champ fait doit être un booléen", ...]

La fonction decrireType() corrige les deux faiblesses de typeof vues plus haut : elle distingue null et les tableaux, qui se cachent tous deux derrière "object". Le validateur, lui, renvoie une liste d’erreurs vide si la tâche est correcte. C’est un patron très répandu : on accumule les problèmes plutôt que de s’arrêter au premier, ce qui donne un retour complet à l’utilisateur.

Étape 7 — Vérification finale

Pour prouver que tout fonctionne de bout en bout, créez une petite liste en mémoire et filtrez les tâches valides. C’est l’embryon de la base de données de CarnetTâches.

const candidates = [
  { titre: "Réviser", fait: false, priorite: 2, etiquettes: ["étude"], echeance: null },
  { titre: "", fait: false, priorite: 1, etiquettes: [], echeance: null },        // titre vide
  { titre: "Lire la doc", fait: true, priorite: 3, etiquettes: ["lecture"], echeance: "2026-06-20" }
];

const valides = candidates.filter(t => validerTache(t).length === 0);
console.log(valides.length);  // 2 — la tâche au titre vide est écartée

Vous devez voir 2 : la tâche au titre vide est rejetée, les deux autres passent. Vous avez maintenant une donnée propre, typée et validée. C’est le socle sur lequel le tutoriel suivant construira des fonctions.

🐞 Pièges fréquents

Symptôme / erreur Cause probable Correctif
typeof t.etiquettes vaut "object" pour un tableau typeof ne distingue pas tableaux et objets Utiliser Array.isArray()
NaN === NaN renvoie false NaN n’est égal à aucune valeur Tester avec Number.isNaN()
Un test if (valeur) rate la valeur "0" "0" est une chaîne non vide, donc truthy Comparer explicitement la valeur attendue
"3" + 1 donne "31" au lieu de 4 + concatène dès qu’un opérande est une chaîne Convertir avec Number() avant d’additionner
Une comparaison == donne un résultat absurde Coercition implicite non transitive Remplacer par ===

🌍 Réalités du terrain

Tout ce tutoriel s’exécute dans la console du navigateur, sans rien installer ni télécharger. C’est un avantage concret quand la connexion est lente ou coûteuse : on peut explorer les types, tester la coercition et déboguer pendant des heures hors ligne, sur n’importe quelle machine, même modeste. Prenez l’habitude d’ouvrir la console pour vérifier une intuition plutôt que de chercher la réponse en ligne — c’est plus rapide et cela ne consomme pas de données.

✅ Récapitulatif

Vous savez désormais reconnaître les sept types primitifs et le type objet, et inspecter une valeur avec typeof tout en connaissant ses deux pièges (null et les tableaux). Vous distinguez null d’undefined, vous testez NaN correctement, et surtout vous anticipez la coercition : vous savez pourquoi "3" + 1 diffère de "3" - 1 et pourquoi on préfère ===. Enfin, vous avez modélisé et validé l’objet tâche de CarnetTâches — la donnée que tout le parcours va manipuler.

🧾 Aide-mémoire

Élément Rôle
typeof x Renvoie le type sous forme de chaîne (sauf null"object")
Array.isArray(x) Teste si x est un tableau
Number.isNaN(x) Teste si x est exactement NaN
Object.is(a, b) Égalité stricte qui gère NaN et -0
=== / !== Égalité stricte, sans coercition (à privilégier)
falsy false, 0, -0, 0n, "", null, undefined, NaN

💪 À vous de jouer

Étendez le validateur pour accepter une échéance au format "AAAA-MM-JJ" uniquement (et la rejeter si la chaîne ne correspond pas), tout en continuant d’autoriser null.

Voir une solution
function echeanceValide(valeur) {
  if (valeur === null) return true;                 // absence assumée : autorisée
  if (typeof valeur !== "string") return false;
  // format AAAA-MM-JJ, puis on vérifie que la date existe vraiment
  if (!/^\d{4}-\d{2}-\d{2}$/.test(valeur)) return false;
  const d = new Date(valeur);
  return !Number.isNaN(d.getTime());                // NaN si date invalide
}

echeanceValide(null);          // true
echeanceValide("2026-06-15");  // true
echeanceValide("15/06/2026");  // false — mauvais format
echeanceValide("2026-13-40");  // false — mois et jour impossibles

Tutoriels liés

Pour aller plus loin

FAQ

Pourquoi typeof null vaut-il "object" ?
C’est un bug des toutes premières versions de JavaScript, conservé depuis pour ne pas casser le code existant. Pour détecter null, comparez directement : valeur === null.

Quand dois-je utiliser bigint plutôt que number ?
Uniquement pour des entiers dépassant Number.MAX_SAFE_INTEGER (environ 9 quadrillions). Pour la quasi-totalité des besoins courants — compteurs, priorités, index — number suffit.

L’égalité permissive == est-elle toujours à proscrire ?
Presque. La seule exception courante et défendable est valeur == null, qui teste null et undefined en une fois. Partout ailleurs, === évite les conversions surprenantes.

Comment convertir proprement une chaîne en nombre ?
Number("12") pour une conversion stricte (renvoie NaN si le texte n’est pas un nombre), ou parseInt/parseFloat si la chaîne contient une unité à ignorer. Vérifiez toujours le résultat avec Number.isNaN().

مشاركة