Close

Via Don Minzoni, 59 - 73025 - Martano (LE)

Gestione degli errori in TypeScript
Web development

Come migliorare la gestione degli errori in TypeScript (senza impazzire)

By, Claudio Poidomani
  • 12 Gen, 2026
  • 39 Views
  • 0 Comment

Nel mondo dello sviluppo web, è facile scrivere codice che funziona, e passare subito alla feature successiva. Ma cosa succede quando quel codice fallisce?

Di recente in Fyonda ci siamo trovati esattamente in questa situazione. Leggendo codice altrui, facendo review, ci siamo accorti di un problema ricorrente: la gestione degli errori in TypeScript è spesso poco chiara e difficile da prevedere. Da questa esperienza sono emersi due approcci che abbiamo testato, migliorato e ora vogliamo condividere. Se scrivi codice in JavaScript o TypeScript, questo articolo fa per te.

_Il problema: “Non ha lanciato errori, quindi pensavo funzionasse”

Immagina di dover usare una funzione che restituisce un Promise<boolean>. A prima vista sembra tutto chiaro. Ma cosa succede quando qualcosa va storto?

Nel mondo JS/TS, ci sono due situazioni critiche che il tipo di ritorno non ti racconta:

  • C’è un throw esplicito nella funzione, ma per scoprirlo occorre leggere la funzione stessa.
  • La funzione può fallire senza un throw esplicito, perché usa internamente API che lanciano eccezioni (ad esempio JSON.parse). Anche in questo caso, devi saperlo a priori o scoprirlo a runtime.

Il punto è che chi usa la funzione non sa se e quando aspettarsi un errore, né da dove possa arrivare. L’unico modo per capirlo è aprire il file e leggere il codice e questo è un problema.

Questa incertezza genera attriti nelle pull request, bug difficili da diagnosticare e assunzioni sbagliate in produzione. Non basta che il codice “funzioni”: deve rendere esplicito come e quando può fallire.

_Soluzione #1: un semplice wrapper try-catch

Per iniziare, abbiamo adottato un wrapper molto semplice che restituisce un array [errore, dato]. È leggero, chiaro e non introduce nuove dipendenze.

Esempio:

type Result<T> = [Error | null, T | null];

async function safe<T>(fn: () => Promise<T>): Promise<Result<T>> {
  try {
    const data = await fn();
    return [null, data];
  } catch (err) {
    return [err as Error, null];
  }
}

// Uso:
const [err, value] = await safe(() => syncStore());
if (err) {
  console.error("Errore nella sincronizzazione:", err);
  return;
}

Pro

  • Chi legge sa che deve gestire l’errore.
  • La struttura è familiare (simile a useState o a certi pattern Python).
  • Il flusso diventa esplicito.

Contro

  • L’errore non è tipizzato: è un Error generico.
  • Potresti dimenticarti di gestirlo correttamente.
  • Non scalabile per progetti complessi.

Per molte situazioni interne, tool rapidi o funzioni semplici, questo approccio basta. Ma volevamo qualcosa di più forte per i moduli core.

_Soluzione #2: tipizzare il fallimento con neverthrow

Per i progetti più grandi, abbiamo esplorato la libreria neverthrow, che porta in TypeScript un pattern stile Rust: restituire un tipo Result invece di fare throw.

Esempio:

import { ok, err, Result } from "neverthrow";

type SyncError =
  | { kind: "ShopifyError"; message: string }
  | { kind: "DbError"; message: string };

async function syncStore(): Promise<Result<boolean, SyncError>> {
  try {
    await callShopify();
    await saveToDb();
    return ok(true);
  } catch (e: any) {
    if (e.response?.status === 404) {
      return err({ kind: "ShopifyError", message: "Negozio non trovato" });
    }
    return err({ kind: "DbError", message: e.message });
  }
}

Uso:

const result = await syncStore();

if (result.isErr()) {
  switch (result.error.kind) {
    case "ShopifyError":
      // gestione specifica
      break;
    case "DbError":
      // gestione diversa
      break;
  }
  return;
}

// Se è tutto ok:
console.log("Sync riuscita:", result.value);

Pro

  • Tipizzazione completa: sai sempre che tipo di errore gestire.
  • Niente più try-catch sparsi.
  • Migliora la leggibilità e la manutenibilità.

Contro

  • Richiede più codice.
  • All’inizio può spaventare (ma è più semplice di quanto sembri).
  • Serve uniformità nel team per trarne il massimo.

In Fyonda lo stiamo introducendo in alcuni moduli chiave, e l’impatto sulla qualità del codice è evidente.

_Quando usare l’uno o l’altro?

ScenarioSoluzione consigliata
Script interni, tool rapidiWrapper try-catch
API, moduli condivisi, librerieneverthrow o approccio simile
Progetto in espansioneMeglio strutturare sin da subito
Refactoring o code review criticheTipizzazione esplicita degli errori

_Conclusione

Gestire gli errori non è un dettaglio tecnico: è un atto di progettazione consapevole per chi legge il tuo codice, per chi ci metterà le mani tra sei mesi, per chi lo usa in produzione e non sa cosa aspettarsi quando qualcosa va storto. Spesso scriviamo codice che “funziona”, ma che non racconta chiaramente cosa succede se qualcosa va storto. È lì che nascono i bug difficili da tracciare, i dubbi in pull request, le patch dell’ultimo minuto. Ecco perché abbiamo deciso di rendere gli errori espliciti, dichiararli nei return type, usare strumenti che ci aiutano a essere più chiari, più strutturati, più affidabili.

Non si tratta di evitare gli errori, ma di accettarli come parte del flusso e progettare il codice in modo che li sappia gestire, comunicarli bene e portarci a una soluzione più pulita.

Recent Comments

Nessun commento da mostrare.

Newest Posts