eduardweb.
TypeScript avansatIntermediar#snippet#tip

Discriminated Unions în TypeScript — pattern-ul care-ți curăță codul API

De Eduard Negru, 21 apr. 2026 · 19 vizualizări · 2 like-uri

Postat acum 1 zi
typescript
type AsyncResult<T, E = Error> =
  | { status: "idle" }
  | { status: "loading" }
  | { status: "success"; data: T }
  | { status: "error"; error: E };

// TypeScript îți forțează să tratezi fiecare caz
function render(r: AsyncResult<User>) {
  switch (r.status) {
    case "idle": return null;
    case "loading": return <Spinner />;
    case "success": return <UserCard user={r.data} />; // r.data există 100%
    case "error": return <Error msg={r.error.message} />; // r.error există 100%
  }
}

De ce nu isLoading + data + error?

Pentru că e unsound — TypeScript nu te împiedică să faci:

if (isLoading) return <Spinner />;
return <UserCard user={data} />; // data poate fi undefined!

Cu Discriminated Union

Compilatorul știe că pe ramura success, data există. Pe ramura error, error există. Fără optional chaining, fără !, fără guard-uri manuale.

Bonus: exhaustive switch

Dacă adaugi o stare nouă (ex: "cancelled"), TypeScript îți spune exact unde te-ai rupt:

function render(r: AsyncResult<User>) {
  switch (r.status) {
    case "idle": return null;
    case "loading": return <Spinner />;
    case "success": return <UserCard user={r.data} />;
    // ❌ Type error: "cancelled" e neacoperit
  }
}

Folosesc ăsta în

  • Rezultate fetch / React Query
  • State machine-uri mici
  • Validare formular (pending / dirty / valid / invalid)
  • Încărcare pagini server-side

Te forțează să gândești stările ca mutuellement exclusive, nu ca flags boolene care pot fi oricum combinate.

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.