eduardweb.
TypeScript avansatAvansat#typescript#type-safety#rbac#advanced

Cum m-au salvat Template Literal Types de bug-urile de tip-o din permisiuni

De Cosmin Rotaru, 6 iul. 2026 · 11 vizualizări · 2 like-uri

Postat 6 iul. 2026
typescript
type Resource = "user" | "billing" | "project";
type Action = "create" | "read" | "update" | "delete";

// Generăm automat toate combinațiile de forma api:resource:action
type Permission = `api:${Resource}:${Action}`;

// Funcție care verifică permisiunea type-safe
function hasAccess(permission: Permission): boolean {
  // Logica de verificare
  return true;
}

// Funcționează perfect
hasAccess("api:billing:read");

// Eroare la compilare: Type '"api:users:read"' is not assignable
// to parameter of type 'Permission'. Did you mean '"api:user:read"'?
hasAccess("api:users:read");

Salutare tuturor. Am avut recent de implementat un sistem de RBAC (Role-Based Access Control) pe un proiect cu peste 80 de rute și vreo 12 resurse diferite. Clasic, am început cu string-uri chioare stocate în baza de date și verificate în frontend, ceva de genul users:write sau billing:read. Toate bune și frumoase până când un coleg a scris din greșeală user:write (la singular) într-un hook de React. Nimic n-a crăpat la build, dar userul n-avea acces la butonul de salvare în producție. Am pierdut vreo două ore căutând de ce nu se trimitea request-ul.

Cum ne salvează TypeScript de typos

Am decis să nu mai trec prin asta și am refăcut tot sistemul folosind Template Literal Types. Dacă nu le-ai folosit până acum, gândește-te la ele ca la template literals din JavaScript, dar mutate complet în lumea tipurilor din TypeScript. Practic, poți genera tipuri noi prin concatenarea altor tipuri de tip string union.

TypeScript este extrem de deștept aici: generează automat produsul cartezian al tuturor combinațiilor posibile. Dacă ai 3 resurse și 4 acțiuni, tipul tău va accepta exact cele 12 string-uri valide. Orice altă variație (cum ar fi singularul greșit de care ziceam mai sus) va genera o eroare de compilare imediat în editor, înainte ca codul să ajungă măcar pe staging.

Cum scalăm abordarea în viața reală

La proiectul menționat, aveam și namespace-uri. Voiam ca permisiunile să aibă formatul api:resource:action. Nicio problemă, le putem compune în continuare. Putem folosi chiar și utilitare native din TS precum Capitalize sau Omit dacă avem excepții de la regulă.

De exemplu, poate resursa billing nu ar trebui să aibă acțiunea delete dintr-un motiv de business. Putem exclude acea combinație specifică folosind utility types ca Exclude. Asta ne oferă o granularitate incredibilă fără să scriem sute de linii de cod de mână.

Trade-off-ul sincer: Atenție la timpii de build

Totul sună minunat pe hârtie, dar am învățat pe pielea mea că există o limită de performanță. TypeScript trebuie să evalueze toate aceste combinații în memorie. Dacă ai 50 de resurse, 6 acțiuni și 4 medii de rulare diferite, ajungi rapid la mii de variante pe care compilatorul trebuie să le valideze la fiecare salvare de fișier.

La un alt proiect, unde am exagerat cu tipuri recursive combinate cu template literals, am observat că timpul de build pe CI/CD a crescut cu aproape 30%, iar serverul de TS din VS Code își dădea restart o dată la o oră din lipsă de memorie. Sfatul meu: folosește-le pentru seturi de date bine definite și finite. Nu încerca să mapezi dinamic tot backend-ul în tipuri TS.

Pentru majoritatea aplicațiilor SaaS de dimensiuni medii, unde ai sub 20-30 de resurse, această abordare este absolut perfectă. Economisești zeci de ore de debugging stupid.

Voi cum gestionați permisiunile în frontend? Mergeți pe string-uri simple și vă bazați pe teste, sau lăsați TypeScript să facă treaba grea?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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