modulo 12 di 24 / Ore 21-40

Moduli 9-16 / Checkpoint 40 ore

Micro-sfida: da Cannot read properties a Strumento non trovato

Come trasformare l'errore tecnico 'Cannot read properties of undefined' in 'STRUMENTO_NON_TROVATO' nel Registro — causa, posizione nel codice e correzione guidata.

25 min Architettura del software

Micro-sfida: da Cannot read properties a Strumento non trovato

Questa lezione appartiene al Modulo 12 — Errori comprensibili.

Obiettivo della lezione

Trasformare un errore JavaScript generico e incomprensibile (Cannot read properties of undefined) in un errore di dominio esplicito (STRUMENTO_NON_TROVATO) nel Registro, capendo dove si trova il problema nel codice e come correggerlo.

Alla fine sai leggere un errore in console, trovare la riga che lo causa e sostituirlo con una verifica esplicita prima che il crash avvenga.

Perché serve nel percorso

Cannot read properties of undefined è l’errore più comune in JavaScript. Compare quando si accede a una proprietà di un valore che si aspettava un oggetto ma era undefined. Nel Registro, questo succede quasi sempre quando si cerca uno strumento o un prestito per ID e non viene trovato — e poi si prova a leggerne lo stato.

Nel Registro del laboratorio

Scenario che causa l’errore:

Un utente inserisce un ID strumento che non esiste più in localStorage (magari cancellato manualmente o non ancora creato). Il codice prova a leggere strumento.stato ma strumento è undefined.

Il codice problematico:

// src/application/registraPrestito.js — PRIMA (fragile)
export function registraPrestito({ strumentoId, studenteId }, archivio) {
  const strumento = archivio.trovaPerId(strumentoId);
  // strumento può essere undefined se non trovato!
  if (strumento.stato !== "disponibile") {  // CRASH: Cannot read properties of undefined
    throw new Error("Non disponibile");
  }
  // ...
}

L’errore in console:

Uncaught TypeError: Cannot read properties of undefined (reading 'stato')
    at registraPrestito (registraPrestito.js:4)
    at HTMLButtonElement.<anonymous> (azioniPrestito.js:12)

L’utente vede la pagina bloccarsi. Non capisce cosa è successo.

La correzione:

// src/application/registraPrestito.js — DOPO (esplicito)
export function registraPrestito({ strumentoId, studenteId }, archivio) {
  const strumento = archivio.trovaPerId(strumentoId);

  // Verifica esplicita PRIMA di accedere alle proprietà
  if (!strumento) {
    throw new Error("STRUMENTO_NON_TROVATO");
  }

  if (strumento.stato === "guasto") {
    throw new Error("STRUMENTO_GUASTO_NON_PRESTABILE");
  }

  if (strumento.stato === "in_prestito") {
    throw new Error("STRUMENTO_GIA_IN_PRESTITO");
  }

  if (strumento.stato !== "disponibile") {
    throw new Error("STRUMENTO_NON_DISPONIBILE");
  }

  // Solo qui accediamo ad altre proprietà, sapendo che strumento esiste
  const prestito = {
    id: crypto.randomUUID(),
    strumentoId: strumento.id,
    studenteId,
    apertura: new Date(),
    chiusura: null
  };

  archivio.aggiorna({ ...strumento, stato: "in_prestito" });
  return prestito;
}

Procedura guidata

  1. Riproduci l’errore: passa un ID strumento inesistente a registraPrestito nel browser.
  2. Leggi il messaggio in console: identifica la riga esatta del crash.
  3. Trova il punto in cui il valore può essere undefined (risultato di trovaPerId, find, filter).
  4. Aggiungi la verifica esplicita (if (!valore) throw new Error("CODICE")) prima di accedere alle proprietà.
  5. Riproduci lo scenario: ora deve apparire un errore leggibile, non un crash.

Attività in classe

Cerca nel codice del Registro tutti i punti in cui si usa un risultato di trovaPerId, find o filter senza verificare prima che non sia undefined:

grep -n "\.trovaPerId\|\.find\|\.filter" src/application/

Per ogni occorrenza trovata, verifica se subito dopo c’è un controllo if (!risultato). Se manca, aggiungilo.

Compila la scheda:

## Micro-sfida: errore → codice esplicito

Errore originale:

File e riga del crash:

Causa (quale variabile era undefined?):

Codice aggiunto:

Errore ora lanciato:

Messaggio utente tradotto:

Spiegazione guidata per studiare

La regola è semplice: mai accedere a una proprietà di un risultato che potrebbe essere undefined senza prima verificarlo.

I metodi che restituiscono undefined quando l’elemento non c’è:

  • Array.find() → restituisce undefined se nessun elemento soddisfa il predicato;
  • Map.get() → restituisce undefined se la chiave non esiste;
  • qualsiasi funzione trovaPerId scritta da te che usa find internamente.

I metodi che non restituiscono undefined (restituiscono array vuoto):

  • Array.filter() → restituisce [] se nessun elemento soddisfa il predicato.

Il pattern corretto dopo ogni ricerca per ID:

const entità = archivio.trovaPerId(id);
if (!entità) {
  throw new Error("ENTITA_NON_TROVATA"); // codice leggibile, non crash
}
// da qui in poi puoi usare entità.qualsiasiproprieta in modo sicuro

Questo pattern vale per strumenti, prestiti e guasti nel Registro.

Perché un codice errore invece di un messaggio? Il codice ("STRUMENTO_NON_TROVATO") viene tradotto dalla UI in italiano. Il dominio non deve sapere come parlare all’utente — si limita a segnalare cosa è andato storto.

Passo per passo nel progetto

  1. Apri src/application/registraPrestito.js.
  2. Trova la riga trovaPerId(strumentoId) e aggiungi il controllo if (!strumento).
  3. Apri src/application/chiudiPrestito.js e fai lo stesso per trovaPerId(prestitoId).
  4. Apri src/application/apriSegnalazioneGuasto.js se esiste e fai lo stesso.
  5. Aggiungi un test per ogni caso:
test("registraPrestito lancia STRUMENTO_NON_TROVATO per ID inesistente", () => {
  const archivio = creaArchivioStrumentiFake([]); // archivio vuoto
  expect(() =>
    registraPrestito({ strumentoId: "non-esiste", studenteId: "stu-01" }, archivio, archivioPrestiti)
  ).toThrow("STRUMENTO_NON_TROVATO");
});

Prova di comprensione

  • Quando Array.find() restituisce undefined?
  • Perché il codice errore ("STRUMENTO_NON_TROVATO") è meglio del messaggio diretto ("Strumento non trovato")?
  • Dove nel codice del Registro è più probabile trovare questo tipo di crash?
  • Come si verifica che la correzione funziona senza usare il browser?

Errori da evitare

  • usare optional chaining (strumento?.stato) invece di una verifica esplicita — nasconde il problema invece di gestirlo;
  • controllare strumento !== null invece di !strumento — undefined non è null in JavaScript;
  • mettere il controllo dopo aver già acceduto a una proprietà dell’oggetto — troppo tardi;
  • lanciare new Error(err.message) invece di un codice semantico — perde informazione.

Prodotto da consegnare

Tutti i casi d’uso del Registro con verifica esplicita dopo trovaPerId e almeno un test che verifica che l’errore corretto viene lanciato per un ID inesistente.

Checklist di chiusura

  • nessun accesso a proprietà di variabile che potrebbe essere undefined senza controllo precedente
  • ogni trovaPerId è seguita da if (!risultato) throw new Error("CODICE")
  • i codici errore usano maiuscole e underscore (STRUMENTO_NON_TROVATO)
  • almeno un test verifica il caso “ID non trovato”
  • il messaggio Cannot read properties of undefined non appare più in console durante l’uso normale

Risultato atteso

Ho trovato il crash in registraPrestito.js alla riga che legge strumento.stato.
Ho aggiunto if (!strumento) throw new Error("STRUMENTO_NON_TROVATO") prima.
Ho fatto lo stesso in chiudiPrestito.js per il prestito non trovato.
I test passano e il crash non compare più in console.
La UI mostra ora "Lo strumento cercato non esiste" invece di un errore tecnico.