Introduzione alla gestione dei valori nulli in JavaScript
Uno dei problemi più comuni e frustranti per chi inizia a programmare in JavaScript è l'incontro con l'errore TypeError: Cannot read property '...' of undefined (o null). Questo errore si verifica quando cerchiamo di accedere a una proprietà di un oggetto che, per qualche motivo, non esiste o non è ancora stato caricato.
Immaginate di lavorare con un'API che restituisce i dati di un utente. Alcuni utenti potrebbero avere un profilo completo, altri potrebbero non aver inserito l'indirizzo, e altri ancora potrebbero non avere nemmeno un oggetto "indirizzo" associato al loro profilo. Se provate a leggere utente.indirizzo.citta e l'oggetto indirizzo è undefined, l'intera applicazione crasherà.
In passato, per evitare questo problema, gli sviluppatori dovevano scrivere lunghe catene di controlli manuali utilizzando l'operatore logico AND (&&). Tuttavia, l'introduzione di ES2020 ha portato due strumenti rivoluzionari: l'Optional Chaining (?.) e il Nullish Coalescing (??). In questa guida esploreremo nel dettaglio cosa sono, perché sono fondamentali e come utilizzarli correttamente per scrivere codice più pulito, robusto e leggibile.
Cos'è l'Optional Chaining (?.)
L'Optional Chaining, rappresentato dall'operatore ?., permette di leggere il valore di una proprietà situata in profondità all'interno di una catena di oggetti senza dover verificare esplicitamente se ogni riferimento nella catena è valido.
Come funziona
Quando utilizzi l'operatore ?., JavaScript controlla se l'operando a sinistra è null o undefined. Se lo è, l'operazione si interrompe immediatamente e l'intera espressione restituisce undefined, invece di lanciare un errore che bloccherebbe l'esecuzione dello script.
Il confronto: Prima vs Dopo
Vediamo come cambia l'approccio alla programmazione. Supponiamo di avere un oggetto utente che potrebbe o meno avere un indirizzo.
const utente = {
nome: "Mario",
profilo: {
email: "mario@example.com"
// Nota: manca la proprietà 'indirizzo'
}
};
// APPROCCIO VECCHIO: Controllo manuale con &&
let cittaVecchio = utente.profilo && utente.profilo.indirizzo && utente.profilo.indirizzo.citta;
console.log(cittaVecchio); // undefined (ma non crasha)
// APPROCCIO MODERNO: Optional Chaining
let cittaNuovo = utente.profilo?.indirizzo?.citta;
console.log(cittaNuovo); // undefined (molto più sintetico!)
In questo esempio, l'operatore ?. verifica se utente.profilo esiste, poi se utente.profilo.indirizzo esiste e infine cerca di accedere a citta. Poiché indirizzo è assente, l'operazione si ferma e restituisce undefined in modo sicuro.
Casi d'uso avanzati: Metodi e Array
L'Optional Chaining non serve solo per le proprietà degli oggetti. Può essere usato anche per chiamare funzioni che potrebbero non essere definite o per accedere a elementi di un array.
- Chiamata a funzioni: Se un oggetto potrebbe avere un metodo opzionale, puoi usare
oggetto.metodo?.(). Se il metodo non esiste, non verrà chiamato e non verrà generato errore. - Accesso agli array: Per accedere a un indice specifico di un array che potrebbe essere nullo, puoi usare
array?.[indice].
Cos'è il Nullish Coalescing (??)
Se l'Optional Chaining ci aiuta a estrarre dati in sicurezza, il Nullish Coalescing ci aiuta a fornire un valore di default quando l'estrazione fallisce o quando un valore è assente.
Il problema dell'operatore OR (||)
Prima del ??, gli sviluppatori usavano l'operatore OR (||) per impostare valori predefiniti. Tuttavia, l'operatore || ha un comportamento rischioso: restituisce il valore di destra se il valore di sinistra è falsy.
In JavaScript, i valori falsy sono: false, 0, "" (stringa vuota), null, undefined e NaN.
Questo crea problemi quando 0 o una stringa vuota sono valori validi per la nostra applicazione. Ad esempio, se vogliamo impostare un volume predefinito a 50, ma l'utente imposta il volume a 0, l'operatore || interpreterà 0 come falsy e imposterà erroneamente il volume a 50.
La soluzione: L'operatore ??
L'operatore Nullish Coalescing (??) è più preciso: restituisce il valore di destra solo se il valore di sinistra è null o undefined. Tutti gli altri valori (inclusi 0, false e le stringhe vuote) vengono considerati validi.
const impostazioniUtente = {
volume: 0,
tema: ""
};
// Problema con l'operatore OR
const volumeDefaultOR = impostazioniUtente.volume || 50;
console.log(volumeDefaultOR); // 50 (Sbagliato! L'utente voleva 0)
const temaDefaultOR = impostazioniUtente.tema || "chiaro";
console.log(temaDefaultOR); // "chiaro" (Sbagliato! L'utente ha scelto una stringa vuota)
// Soluzione con Nullish Coalescing
const volumeDefaultNullish = impostazioniUtente.volume ?? 50;
console.log(volumeDefaultNullish); // 0 (Corretto!)
const temaDefaultNullish = impostazioniUtente.tema ?? "chiaro";
console.log(temaDefaultNullish); // "" (Corretto!)
Combinare Optional Chaining e Nullish Coalescing
La vera potenza di questi due operatori emerge quando vengono utilizzati insieme. Questa combinazione permette di navigare in strutture dati complesse e fornire un valore di fallback in un'unica riga di codice leggibile.
Esempio di integrazione
Immaginiamo di dover visualizzare il nome della città di un utente in un'interfaccia web. Se l'utente non ha inserito la città, o se l'oggetto indirizzo è mancante, vogliamo mostrare il testo "Città non specificata".
const utente = {
nome: "Alice",
contatti: {
social: {
twitter: "@alice_dev"
}
// Manca l'oggetto indirizzo
}
};
// Estraiamo la città in modo sicuro e forniamo un default
const cittaVisualizzata = utente.contatti?.indirizzo?.citta ?? "Città non specificata";
console.log(`L'utente vive a: ${cittaVisualizzata}`);
// Output: L'utente vive a: Città non specificata
In questo esempio, utente.contatti?.indirizzo?.citta valuta a undefined. L'operatore ?? interviene e assegna il valore di default, evitando crash e garantendo una user experience coerente.
Esempi pratici in scenari reali
1. Gestione delle API REST
Quando si consumano API, non possiamo mai essere certi al 100% che ogni campo sia presente in ogni risposta. L'Optional Chaining è fondamentale nei componenti React o Vue per evitare che l'intera pagina vada in crash durante il caricamento dei dati.
async function caricaProfilo() {
const response = await fetch('https://api.esempio.it/user/1');
const data = await response.json();
// Visualizziamo il nome dell'azienda dell'utente se esiste
const azienda = data.user?.work?.company?.name ?? "Disoccupato/Freelance";
console.log(`Lavora presso: ${azienda}`);
}
2. Configurazioni di un'applicazione
Spesso le app hanno un file di configurazione globale dove l'utente può sovrascrivere solo alcune impostazioni. Il Nullish Coalescing è perfetto per gestire i valori di default.
const configDiDefault = {
timeout: 3000,
tentativi: 3,
debug: true
};
const configUtente = {
timeout: 0, // L'utente vuole disabilitare il timeout
debug: false
};
const configFinale = {
timeout: configUtente.timeout ?? configDiDefault.timeout,
tentativi: configUtente.tentativi ?? configDiDefault.tentativi,
debug: configUtente.debug ?? configDiDefault.debug
};
console.log(configFinale.timeout); // 0 (grazie a ??), sarebbe stato 3000 con ||
Errori comuni e FAQ
Posso usare ?. per assegnare un valore?
No. L'Optional Chaining è un operatore di lettura. Non puoi usarlo sul lato sinistro di un'assegnazione.
Sbagliato: utente?.nome = "Luca";
Corretto: if (utente) { utente.nome = "Luca"; }
Qual è la differenza tra || e ?? in sintesi?
||(OR): Restituisce il valore di destra se il sinistro è qualsiasi valore falsy (0,"",false,null,undefined,NaN).??(Nullish): Restituisce il valore di destra solo se il sinistro ènulloundefined.
Posso concatenare ?. e || insieme?
Sì, è possibile, ma è sconsigliato se lavori con numeri o booleani, poiché l'operatore || potrebbe sovrascrivere valori validi come 0 o false. Usa sempre ?? dopo un ?. per una gestione più precisa.
Prossimi passi
Ora che hai padroneggiato l'Optional Chaining e il Nullish Coalescing, il tuo codice JavaScript sarà molto più resiliente agli errori di runtime. Per continuare il tuo percorso di apprendimento, ti suggeriamo di approfondire i seguenti argomenti:
- Destructuring Assignment: Impara a estrarre valori da oggetti e array in modo ancora più efficiente.
- Async/Await e Try-Catch: Se l'Optional Chaining gestisce i dati mancanti, il blocco
try-catchè essenziale per gestire errori critici durante le chiamate di rete. - TypeScript: Se vuoi eliminare completamente l'incertezza sui tipi di dati (sapere esattamente se una proprietà può essere
nulloundefined), TypeScript è il passo naturale successivo.
Ricorda che la chiave per scrivere codice professionale non è solo farlo funzionare, ma renderlo prevedibile e facile da mantenere per te e per i tuoi colleghi.