Accède en toute sécurité aux propriétés profondément imbriquées avec le chaînage optionnel (?.) et fournis des valeurs par défaut avec la coalescence nulle (??).
Accéder à des propriétés imbriquées sur null ou undefined génère une TypeError — l'une des erreurs les plus courantes en JavaScript.
const user = { name: 'Alice' };
console.log(user.address.city);
// TypeError: Cannot read properties of undefined (reading 'city')Solution traditionnelle — vérifier chaque niveau :
if (user && user.address && user.address.city) {
console.log(user.address.city);
}
// Ou avec un ternaire
const city = user && user.address ? user.address.city : 'Unknown';C'est verbeux et ça empire avec une imbrication plus profonde :
if (user && user.company && user.company.address && user.company.address.zip) {
// enfin sûr d'utiliser user.company.address.zip
}JavaScript moderne fournit deux opérateurs élégants pour résoudre ce problème : le chaînage optionnel (?.) et la coalescence nulle (??).
L'opérateur de chaînage optionnel (?.) retourne undefined au lieu de générer une erreur lors de l'accès à une propriété sur null ou undefined.
Accès aux propriétés :
const user = { name: 'Alice' };
console.log(user.address?.city); // undefined (pas d'erreur !)
console.log(user.address?.city?.length); // undefinedNotation entre crochets :
const key = 'city';
console.log(user.address?.[key]); // undefinedAppels de méthode :
const user = { name: 'Alice' };
console.log(user.greet?.()); // undefined (pas d'erreur même si greet n'existe pas)
const admin = {
name: 'Bob',
greet() { return `Hi, I'm ${this.name}`; }
};
console.log(admin.greet?.()); // "Hi, I'm Bob"Comportement de court-circuit : Quand le côté gauche est null ou undefined, toute la chaîne s'arrête et retourne undefined. Rien à droite n'est évalué :
const user = null;
console.log(user?.address?.city); // undefined (pas d'erreur)
// user est null, donc .address n'est jamais accédéNote : Le chaînage optionnel ne vérifie que null et undefined, pas les autres valeurs falsy comme 0, '', ou false.
L'opérateur de coalescence nulle (??) fournit une valeur par défaut quand le côté gauche est null ou undefined.
const name = null ?? 'Anonymous';
console.log(name); // 'Anonymous'
const score = undefined ?? 0;
console.log(score); // 0Différence clé avec || :
L'opérateur || retourne le côté droit pour toute valeur falsy (null, undefined, 0, '', false, NaN). L'opérateur ?? ne se déclenche que sur null et undefined :
// || traite 0, '', false comme "manquants"
0 || 42; // 42 (0 est falsy)
'' || 'hello'; // 'hello' ('' est falsy)
false || true; // true (false est falsy)
// ?? traite seulement null/undefined comme "manquants"
0 ?? 42; // 0 (0 n'est PAS null/undefined)
'' ?? 'hello'; // '' ('' n'est PAS null/undefined)
false ?? true; // false (false n'est PAS null/undefined)Combiner avec le chaînage optionnel :
const city = user?.address?.city ?? 'Unknown';
// Si user, address, ou city est manquant → 'Unknown'Opérateurs d'affectation logique (ES2021) :
let a = null;
a ??= 'default'; // a vaut maintenant 'default' (était null)
let b = 0;
b ??= 42; // b vaut toujours 0 (0 n'est pas null/undefined)
let c = '';
c ||= 'fallback'; // c vaut 'fallback' ('' est falsy)
let d = 5;
d &&= d * 2; // d vaut 10 (5 est truthy)Le chaînage optionnel et la coalescence nulle brillent dans des scénarios réels.
Réponses API avec champs optionnels :
// L'API peut retourner des données partielles
const response = await fetchUser(id);
const avatar = response?.data?.user?.avatar ?? '/default-avatar.png';
const bio = response?.data?.user?.bio ?? 'No bio provided';Objets de configuration avec valeurs par défaut :
function createApp(config) {
const port = config?.server?.port ?? 3000;
const host = config?.server?.host ?? 'localhost';
const debug = config?.debug ?? false;
console.log(`Starting on ${host}:${port}`);
}
createApp({ server: { port: 8080 } });
// Starting on localhost:8080
createApp(undefined);
// Starting on localhost:3000Chaînes d'éléments DOM :
// Accéder en toute sécurité aux éléments DOM imbriqués
const text = document.querySelector('.card')?.querySelector('.title')?.textContent ?? '';Chaîner plusieurs opérateurs ?. :
const users = [
{ name: 'Alice', address: { city: 'Paris' } },
{ name: 'Bob' }, // pas d'adresse
null, // entrée null
];
const cities = users.map(u => u?.address?.city ?? 'Unknown');
console.log(cities); // ['Paris', 'Unknown', 'Unknown']<div id="output"></div>
<script>
// Avant : vérifications null verbeuses
const user1 = { name: 'Alice', address: { city: 'Paris' } };
const user2 = { name: 'Bob' };
const user3 = null;
// Après : chaînage optionnel propre + valeurs par défaut
const city1 = user1?.address?.city ?? 'Unknown';
const city2 = user2?.address?.city ?? 'Unknown';
const city3 = user3?.address?.city ?? 'Unknown';
// ?? vs || avec des valeurs falsy
const score = 0;
const withOr = score || 'no score'; // 'no score' (faux !)
const withQQ = score ?? 'no score'; // 0 (correct !)
// Appel de méthode optionnel
const greet = user1?.sayHello?.() ?? 'No greeting method';
const output = document.getElementById('output');
output.innerHTML = `
<p>City 1: ${city1}</p>
<p>City 2: ${city2}</p>
<p>City 3: ${city3}</p>
<p>score || default: ${withOr}</p>
<p>score ?? default: ${withQQ}</p>
<p>Optional method: ${greet}</p>
`;
</script>Que retourne `0 ?? 42` ?