TypeScript avansat: branded types, exhaustive checks, satisfies
TypeScript a depasit faza in care era doar JavaScript cu tipuri. In 2026, o codebaza serioasa foloseste branded types pentru identitate, exhaustive checks pentru flow control sigur, operatorul satisfies pentru inferenta precisa si template literal types pentru API-uri care imposibil sa fie chemate gresit. Cine ramane la string si number plain pierde garantiile pe care limbajul ti le ofera deja gratuit.
Multe echipe scriu TypeScript ca pe Java fara generics. Folosesc any cand devine greu, lasa props ca string in loc sa modeleze stari concrete si descopera bug-uri la runtime care puteau fi prinse de compilator. Diferenta intre o codebaza fragila si una solida nu este disciplina la review, ci tipuri care fac imposibila apelarea gresita.
Articolul de fata trece prin patternuri pe care firma de IT Blackbone le foloseste in productie pentru produse cu volume mari de cod si echipe distribuite. Vei vedea cum branded types elimina confuzii intre ID-uri, cum exhaustive checks fac switch-urile sigure pentru viitor, cum satisfies inlocuieste type assertions periculoase si cum template literal types creeaza API-uri auto-documentate.
01Branded types: identitati care nu se amesteca
Cea mai comuna sursa de bug-uri intr-o codebaza mare este amestecarea identificatorilor. UserId si OrderId sunt ambele string in TypeScript de baza, deci compilatorul te lasa sa pasezi un OrderId acolo unde se asteapta UserId. Rezultatul este un bug subtil care apare la runtime, in productie, cand o functie cauta o factura cu ID-ul unui client.
Branded types rezolva problema. Definesti un type care combina string-ul cu o marca unica accesibila doar prin syntaxa nominala. UserId devine string cu marca _user, OrderId devine string cu marca _order. Cele doua tipuri sunt structural diferite si compilatorul refuza sa le confunde. Crearea unui branded type se face printr-o functie creator care valideaza si returneaza tipul.
Echipa Blackbone foloseste branded types pentru toate identificatorii, pentru email-uri validate, pentru token-uri JWT si pentru orice valoare care are reguli de validare. Costul este minim, doar o functie creator per tip, dar beneficiul este uriaș: zero confuzii la apel, refactoring sigur si claritate maxima in semnatura functiilor.
type UserId = string & { readonly _user: unique symbol }; const userId = (s: string) => s as UserId. Simplu, ieftin, salvator de bug-uri grele.
02Discriminated unions pentru stari aplicatie
Cand modelezi o stare cu loading, success si error, multi developeri scriu un singur tip cu trei booleani si trei field-uri opționale. Rezultatul este o stare imposibil de inteles si plina de validari runtime. Discriminated unions inlocuiesc acest dezastru cu un union curat in care fiecare varianta are exact field-urile relevante.
Pattern-ul este simplu. Defines un type Status cu trei membri: idle, loading, success cu data, error cu message. Field-ul status este discriminantul. Cand verifici if status.status equals success, TypeScript stie automat ca data exista. Cand verifici error, stie ca message exista. Nu mai ai validari de optional chaining peste tot, ai control flow precis.
Firma de IT Blackbone modeleaza in acest fel toate fluxurile de fetching, toate sincronizarile background, toate state machines mici din UI. Combinatia cu pattern matching prin switch-uri exhaustive elimina o categorie intreaga de bug-uri si face codul atat de citibil incat review-urile devin rapide si placute.
- →Un discriminator field clar, de obicei status sau kind
- →Field-uri specifice fiecarei variante, nu opționale globale
- →Pattern matching cu switch peste discriminator
- →Zero optional chaining pe field-uri care exista garantat
- →Refactoring sigur, compilatorul prinde tot
03Exhaustive checks: switch-uri care nu te tradeaza
Cand ai un union de N variante si scrii un switch, este usor sa uiti o varianta cand adaugi ulterior una noua. TypeScript te poate proteja daca ii ceri politicos prin pattern-ul never. Functia assertNever primeste un never si arunca o eroare. La default-ul switch-ului apelezi assertNever cu valoarea, iar compilatorul iti spune daca a ramas vreo varianta neacoperita.
Magia este ca atunci cand toate cazurile sunt acoperite, valoarea ramasa la default este never, iar functia accepta. Cand adaugi o varianta noua in union si uiti sa o tratezi in switch, valoarea ramasa este chiar acea varianta noua, nu never, iar compilatorul refuza sa compileze. Iti spune exact unde si ce ai uitat.
Echipa Blackbone foloseste assertNever in fiecare reducer, fiecare event handler complex si fiecare API gateway. Costul este o linie de cod. Beneficiul este ca refactoringele care adauga stari noi devin sigure: schimbi tipul si compilatorul iti deschide o lista cu fiecare loc care trebuie actualizat. Nu mai uiti niciodata o varianta.
04Operatorul satisfies: inferenta precisa fara compromisuri
Inainte de satisfies, aveai doua optiuni cand defineai o constanta complexa. Sau o tipai cu un type larg si pierdeai inferenta exacta, sau o lasai inferata si pierdeai validarea structurii. Satisfies rezolva dilema: declari o valoare, ii dai constraint-ul cu satisfies, iar TypeScript valideaza structura si pastreaza inferenta exacta.
Exemplul tipic este o harta de configurare. Vrei sa stii ca toate cheile sunt din un set finit si ca valorile au o structura definita, dar vrei sa stii la consum care chei concrete au fost folosite, nu doar tipul lor abstract. Cu as const plus satisfies obtii ambele: validare la declarare si inferenta literala la consum.
Firma de IT Blackbone aplica satisfies pentru route configs, theme objects, feature flags si orice obiect mare care trebuie validat structural fara pierderea informatiei specifice. Operatorul a inlocuit complet pattern-ul as Type, care era o type assertion potential periculoasa. Diferenta in calitatea inferentei este vizibila imediat in editor.
- →Valideaza structura fara sa pierzi tipuri literale
- →Inlocuieste as Type in 90 la suta din cazuri
- →Combinat cu as const da inferenta perfecta
- →Esential pentru route configs si feature flags
- →Compilator-friendly, fara unsafe casts
05Template literal types pentru API-uri auto-documentate
Template literal types iti dau capacitatea sa construiesti tipuri din string-uri concrete combinate. Daca ai un set de resurse si un set de actiuni, poti construi un tip Permission care combina toate combinatiile valide. Rezultatul este ca un parametru de functie care cere o Permission accepta doar string-uri valide, generate automat din alte tipuri.
Aplicatia practica este la API clients, la sisteme de permisiuni, la query builders si la rute. Defines un set de rute cu parametri, iar tipul rezultant este o uniune de toate combinatiile concrete. Un developer care apeleaza navigateTo nu poate scrie o ruta inexistenta, pentru ca compilatorul accepta doar tipurile generate.
Echipa Blackbone foloseste template literal types pentru route-uri in App Router, pentru chei de traducere in i18n si pentru permisiuni in sisteme RBAC complexe. Bonus, atunci cand schimbi un endpoint sau o cheie de traducere, compilatorul listeaza toate locurile care trebuie modificate. Refactoringul devine o conversatie cu compilatorul, nu o cautare cu grep.
06Combinatii in productie: cum arata cod TypeScript matur
Patternurile de mai sus nu traiesc separat. Un API client matur folosește branded types pentru ID-uri, discriminated unions pentru rezultate, satisfies pentru configuratie, template literals pentru endpoint-uri si exhaustive checks in handlere de eroare. Combinatia produce un layer de date in care imposibilitatea de a face apeluri gresite este garantata de compilator.
Aceasta abordare reduce dramatic bug-urile de runtime. Codul devine mai usor de citit, mai usor de refactorizat si mai usor de extins. Onboarding-ul noilor developeri se accelereaza pentru ca tipurile documenteaza singure ce este permis. Code review-ul scade in volum pentru ca compilatorul prinde clase intregi de erori.
Firma de IT Blackbone aplica aceste patternuri in toate proiectele noi si in refactoringele majore ale celor mostenite. Diferenta intre o codebaza care le foloseste si una care nu este uriasa: prima livreaza features rapid si fara regressii, a doua se opreste din evolutie pentru ca fiecare modificare risca sa rupa altceva. Investitia in tipuri precise se intoarce in fiecare zi de dezvoltare.
Concluzii
TypeScript avansat nu inseamna trucuri obscure cu generics. Inseamna patternuri solide care modeleaza realitatea aplicatiei si lasa compilatorul sa lucreze pentru tine. Branded types, discriminated unions, exhaustive checks, satisfies si template literals sunt cele cinci instrumente care fac diferenta intre cod fragil si cod matur.
Daca echipa ta scrie TypeScript dar nu foloseste aceste patternuri, ai un upside imens disponibil fara sa schimbi tehnologia. Echipa Blackbone face training-uri si refactoringuri care aduc codebases la acest standard in cateva sprinturi. Investitia se intoarce in mai putine bug-uri si mai multa viteza.
Modernizeaza codebaza ta TypeScript
Echipa Blackbone face audit, refactoring si training pentru TypeScript avansat. De la branded types la API-uri auto-documentate, in livrabile concrete.
Discută cu Blackbone
