eduardweb.
TypeScript avansatIntermediar#architecture#typescript#clean-code

Discriminated unions în TypeScript: Pattern-ul care mi-a salvat proiectele de bug-uri în producție

De Delia Petre, 2 iul. 2026 · 11 vizualizări · 2 like-uri

Postat 2 iul. 2026
typescript
type FetchState<T> =
  | { status: 'idle' }
  | { status: 'loading' }
  | { status: 'success'; data: T; updatedAt: number }
  | { status: 'error'; error: Error };

function renderUI<T>(state: FetchState<T>) {
  switch (state.status) {
    case 'idle':
      return 'Așteaptă acțiunea...';
    case 'loading':
      return 'Se încarcă...';
    case 'error':
      // TypeScript știe sigur că aici avem proprietatea 'error'
      return `Eroare: ${state.error.message}`;
    case 'success':
      // TypeScript știe sigur că aici avem 'data' și 'updatedAt'
      return `Date actualizate la ${state.updatedAt}: ${JSON.stringify(state.data)}`;
  }
}

Salutare tuturor. Astăzi vreau să vorbim despre probabil cel mai util feature din TypeScript pe care îl folosesc în absolut orice proiect: discriminated unions (sau tagged unions). Dacă încă scrii interfețe cu zece câmpuri opționale și faci verificări cu if-uri chioare sperând că o proprietate e acolo la runtime, te chinui singur fără motiv.

Cum m-am lovit de problemă

Acum vreo doi ani lucram la un dashboard pentru o aplicație de monitorizare industrială. Aveam în jur de 15k de senzori care trimiteau date în timp real prin WebSocket. State-ul aplicației în frontend era un coșmar. Am început clasic: o singură interfață mare pentru state-ul paginii care avea proprietăți gen isLoading, error, data, retryCount.

Problema era evidentă. Când isLoading era true, data putea fi undefined sau putea conține date vechi de la sesiunea precedentă. Codul era plin de aserțiuni urâte, gen data!.value sau as any. Am avut cel puțin trei crash-uri urâte în producție din cauza asta. Atunci m-am enervat și am rescris totul folosind discriminated unions. Am eliminat complet stările imposibile și am redus codul de handling cu vreo 30%.

Cum funcționează de fapt?

Ideea e simplă. Definești tipuri specifice pentru fiecare stare posibilă și le pui un câmp comun (de obicei numit type, status sau kind) care are ca valoare un string literal. TypeScript folosește acest câmp ca pe o cheie de discriminare.

Când folosești un switch sau un if pe acel câmp comun, TypeScript face narrowing automat. În interiorul acelei ramuri de cod, compilatorul știe exact ce proprietăți există și ce proprietăți nu au cum să existe fizic în obiect. Nu mai ai nevoie de semne de întrebare peste tot sau de aserțiuni forțate.

Unde strălucește pattern-ul ăsta?

În primul rând, la răspunsurile de la API. În loc de o structură generică de răspuns, folosesc o uniune între un tip de succes și unul de eroare. Dacă statusul e 'error', am acces doar la obiectul de eroare; dacă e 'success', am acces doar la date.

În al doilea rând, la stările din UI. Loading, Success, Error, Empty. Fiecare stare este un tip separat. UI-ul devine o funcție pură care randează exact ce trebuie pentru starea respectivă, fără să mai ghicim dacă avem sau nu date în timp ce se încarcă pagina.

În al treilea rând, la reducer actions. În Redux sau useReducer din React, acțiunile sunt exemplul perfect. Fiecare acțiune are un type și un payload specific acelei acțiuni, iar reducer-ul devine extrem de ușor de urmărit.

Trade-off-ul sincer

Nimic nu e perfect în meseria asta. Cel mai mare minus pe care l-am observat apare când lucrezi cu API-uri legacy scrise prost. Dacă backend-ul îți trimite formate inconsistente (de exemplu, returnează un array gol în loc de obiect în caz de eroare, dar păstrează status HTTP 200), TypeScript nu te poate proteja la runtime.

Va trebui să scrii validatoare manuale la intrarea datelor în aplicație (folosind librării gen Zod sau Valibot). Asta înseamnă cod în plus de scris și de întreținut. Pe proiecte mici, poate părea un overhead inutil, dar la proiecte mari te salvează de nopți pierdute.

Voi folosiți pattern-ul ăsta pentru stările din aplicație sau preferați să mergeți pe abordarea clasică cu proprietăți opționale?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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