?. et ?? : travailler avec des données qui peuvent manquer
Accédez sans erreur à une donnée qui peut manquer avec ?., fournissez une valeur de secours avec ??, et cessez de perdre les 0 et les chaînes vides à cause de ||.
Où en êtes-vous avec cette notion ?
Une indication personnelle, enregistrée uniquement dans ce navigateur.
À lire ensuite
Le format café
L’essentiel à comprendre, le temps d’un café.
Envie de creuser ? Un cours plus complet vous attend juste après, à déplier sans quitter cette page.
Ce que vous allez comprendre
Comment lire une donnée qui peut manquer sans interrompre votre script, et comment fournir une valeur de remplacement sans écraser un 0 parfaitement légitime.
Le problème
Une réponse d’API ne contient pas toujours ce que vous attendez.
const utilisateur = { name: 'Jean' };
console.log(utilisateur.address.city);
// TypeError : lecture de « city » sur undefined
address vaut undefined : l’accès suivant échoue et arrête l’exécution.
?. protège l’accès
Le chaînage optionnel dit « continue seulement si la valeur existe ».
const ville = utilisateur.address?.city;
// undefined, sans erreur
address existe → on lit city
address absent → on s’arrête, résultat undefined
Vous obtenez undefined au lieu d’une exception. C’est précieux sur des champs réellement facultatifs.
?? fournit une valeur de secours
Le coalescement nul remplace la valeur uniquement si elle vaut null ou undefined.
const ville = utilisateur.address?.city ?? 'Ville inconnue';
La différence décisive avec ||
|| remplace toute valeur « falsy » : 0, la chaîne vide, false, null, undefined. C’est souvent un bug silencieux.
const stock = 0;
console.log(stock || 10); // 10, le stock réel est perdu
console.log(stock ?? 10); // 0, correct
Les deux opérateurs ne posent pas la même question. || demande « est-ce vide ou faux ? ». ?? demande « est-ce absent ? ». Sur un prix, un stock, une note ou une case décochée, seule la seconde question est généralement la bonne.
Fiche mémo
a?.b— siavautnullouundefined, l’expression vautundefinedau lieu de lever une erreur.a ?? b—bn’est utilisé que siavautnullouundefined.a || b—best utilisé dès queaest falsy, y compris0et la chaîne vide.- Donnée d’API facultative :
?.pour l’accès, puis??pour le repli, dans cet ordre.
L’idée à emporter : ?. protège l’accès à une donnée, ?? fournit une valeur de secours, et seul ?? respecte un 0 ou une chaîne vide voulus.
Et si on allait plus loin ?
Le café vous a donné les repères. Prenez maintenant le temps de comprendre les mécanismes et de pratiquer, si vous le souhaitez.
Aller plus loinChaînage optionnel : appels, index et court-circuit sans mauvaise surpriseDévelopper le coursReplier le cours
Ce que vous saurez faire
Protéger un appel de méthode et un accès indexé, prévoir jusqu’où ?. court-circuite une expression, et écrire un mélange ?? et || que le moteur accepte. Sauf mention contraire, les exemples s’exécutent tels quels dans la console d’un navigateur récent ou dans Node.js 18 ou plus, sans aucune dépendance.
1. Trois formes, pas une seule
L’original ne montre que l’accès à une propriété. Le chaînage optionnel s’écrit en réalité de trois manières.
const config = {
nom: 'atelier',
etiquettes: ['neuf'],
decrire() {
return 'atelier pret';
},
};
console.log(config.reglages?.theme); // undefined
console.log(config.decrire?.()); // "atelier pret"
console.log(config.formater?.()); // undefined
console.log(config.etiquettes?.[0]); // "neuf"
console.log(config.avis?.[0]); // undefined
Résultat attendu dans la console, dans l’ordre : undefined, atelier pret, undefined, neuf, undefined.
objet.propriete?.suiteprotège un accès.objet.methode?.()n’appelle la fonction que simethodeexiste. Attention : simethodeexiste mais n’est pas une fonction, par exemple un nombre, l’appel échoue quand même avec uneTypeError.?.()teste l’absence, pas le type.tableau?.[0]protège un accès indexé, y compris avec une clé calculée.
Référence : chaînage optionnel.
2. Le court-circuit va plus loin que l’accès suivant
Quand la valeur testée vaut null ou undefined, le moteur abandonne toute la suite de la chaîne, pas seulement la propriété qui suit immédiatement.
function trace(valeur) {
console.log('trace appelée');
return valeur;
}
const vide = null;
console.log(vide?.liste[trace(0)].nom);
// undefined, et « trace appelée » ne s’affiche jamais
Une parenthèse met fin à la chaîne. Le court-circuit ne franchit pas cette limite :
const donnees = { liste: null };
console.log(donnees.liste?.[0]); // undefined
console.log((donnees.liste?.[0]).nom); // TypeError
Dans la seconde ligne, la parenthèse produit d’abord undefined, puis .nom est évalué normalement sur cette valeur : l’erreur revient.
3. ?? refuse de cohabiter avec && et ||
Écrire a ?? b || c est une erreur de syntaxe, refusée à l’analyse du fichier. Ce n’est pas une préférence de style : le fichier entier ne s’exécute pas, avant même que la première ligne ne tourne.
const a = null;
const b = 0;
const c = 'repli';
// const valeur = a ?? b || c; // SyntaxError si vous décommentez cette ligne
console.log((a ?? b) || c); // "repli"
console.log(a ?? (b || c)); // "repli"
console.log((a ?? b) ?? c); // 0
Les parenthèses sont obligatoires parce que les deux lectures possibles ne donnent pas le même résultat dans le cas général. Le langage préfère vous obliger à choisir plutôt que d’imposer une priorité que personne ne retiendrait.
Référence : coalescement nul.
Pièges et limites
?.ne valide pas les données. Il évite une exception, il ne garantit pas que la réponse d’une API a la forme attendue. Un champ renommé côté serveur produira desundefinedsilencieux au lieu d’une erreur visible. Sur une frontière réseau, vérifiez la structure reçue avant de l’utiliser.- Trop de
?.masque un vrai défaut. Si un champ ne devrait jamais manquer, une erreur bruyante est plus utile qu’un affichage vide. ??ne traite pas la chaîne vide.'' ?? 'défaut'vaut la chaîne vide. Lorsque votre règle métier considère qu’une chaîne vide équivaut à une absence,||est le bon opérateur : le choix dépend du sens, pas de la mode.?.ne s’utilise pas à gauche d’une affectation.objet.a?.b = 1est refusé. Le chaînage optionnel sert à lire, pas à écrire.
À vous de jouer
À partir de cette réponse, produisez quatre valeurs sans provoquer d’erreur.
const reponse = {
produit: { nom: 'Clavier', remise: 0, etiquette: '' },
fabricant: null,
};
- Le nom du fabricant, ou
'Fabricant inconnu's’il est absent. - La remise, ou
5si elle est absente. Une remise de0doit rester0. - L’étiquette, ou
'Sans étiquette'. Ici, une chaîne vide compte comme une absence d’étiquette. - Le premier avis, sachant que la propriété
avisn’existe pas.
Correction commentée
const fabricant = reponse.fabricant?.nom ?? 'Fabricant inconnu';
const remise = reponse.produit?.remise ?? 5;
const etiquette = reponse.produit?.etiquette || 'Sans étiquette';
const premierAvis = reponse.avis?.[0];
console.log(fabricant, remise, etiquette, premierAvis);
// Fabricant inconnu 0 Sans étiquette undefined
Ligne 1 : fabricant vaut null, donc ?.nom court-circuite et renvoie undefined, que ?? remplace par le texte de repli.
Ligne 2 : c’est le cas décisif. remise vaut 0, une valeur réelle. ?? ne déclenche le repli que sur null et undefined : le 0 est conservé. Avec || 5, la remise nulle serait devenue cinq pour cent, sans le moindre message d’erreur.
Ligne 3 : ici || est volontaire et correct. La règle énoncée dit qu’une chaîne vide vaut absence d’étiquette ; ?? aurait laissé passer la chaîne vide et affiché un espace blanc. L’opérateur juste est celui qui correspond à la règle, pas celui qui est le plus récent.
Ligne 4 : avis n’existe pas, mais ?.[0] protège l’accès indexé et renvoie undefined au lieu de lever une TypeError.
Dernier point : si vous aviez voulu écrire ces règles en une seule expression mélangeant ?? et ||, il aurait fallu des parenthèses explicites, faute de quoi le fichier n’aurait pas été analysé.
Vous pouvez aussi vous arrêter ici. L’approfondissement est facultatif.
Les repères Cours Café
Pour aller à la source
Documentation de référence. Vérification éditoriale encore à effectuer.
Gardez une trace de cette idée.
Favoris, notes et progression seront disponibles après connexion du stockage distant.