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

Template literal types în TypeScript: permisiuni granulare fără typo-uri

De Ana Ionescu, 14 aug. 2026 · 3 vizualizări · 3 like-uri

Postat acum 3 zile
typescript
type Scope = 'api' | 'internal';
type Resource = 'user' | 'invoice' | 'project';
type Action = 'read' | 'create' | 'update' | 'delete';

// Generează: "api:user:read" | "api:user:create" | ...
export type Permission = `${Scope}:${Resource}:${Action}`;

export function hasPermission(raw: string): raw is Permission {
  const parts = raw.split(':');
  if (parts.length !== 3) return false;
  
  const [scope, resource, action] = parts;
  const validScopes: Scope[] = ['api', 'internal'];
  const validResources: Resource[] = ['user', 'invoice', 'project'];
  const validActions: Action[] = ['read', 'create', 'update', 'delete'];

  return (
    validScopes.includes(scope as Scope) &&
    validResources.includes(resource as Resource) &&
    validActions.includes(action as Action)
  );
}

Am bușit o dată un deployment vineri seara pentru că cineva a scris users:delete în loc de user:delete într-un middleware de permisiuni. Codul a compilat perfect, testele unitare pe logică au trecut, dar în producție un rol critic nu mai putea șterge nimic. Acolo am decis să nu mai lăsăm niciun string de autorizare neverificat de compilator.

Template literal types (introduse în TypeScript 4.1) rezolvă exact problema asta: transformă concatenarea de string-uri într-un mecanism strict de verificare la compilare.

Cum funcționează combinarea automată

Frumusețea la template literals este că TypeScript face produs cartezian automat pe union types. Dacă definești o listă de resurse și o listă de acțiuni, compilatorul generează toate permutările valide fără să scrii tu manual zeci de string-uri.

La un proiect cu vreo 12 resurse și 4 operațiuni CRUD standard, am trecut de la 48 de constante scrise de mână la două union types curate. Când am adăugat ulterior un nou scope (admin vs tenant), n-a trebuit decât să modific definiția de bază, iar TypeScript a actualizat instant toate cele 96 de variante valide în autocomplete.

Validare la runtime cu type predicate

Tipul static te ajută când scrii cod în backend sau frontend, dar permisiunile vin de multe ori dintr-un token JWT sau din baza de date ca string simplu. Aici intervine o funcție de narrowing (type guard):

Verifici dacă string-ul primit respectă pattern-ul înainte să-l pasezi mai departe în logica de business. Dacă payload-ul conține o mizerie de genul api:billing:drop_tables, type guard-ul întoarce false și blochezi request-ul înainte să ajungă în controller.

Trade-off-ul real: Explozia combinatorică

Nu tot ce zboară se combină. Dacă ai 50 de entități în sistem, 10 acțiuni posibile și 4 niveluri de granularitate (own, team, org, global), TypeScript trebuie să țină minte 2.000 de tipuri literale într-un singur union.

La peste câteva mii de combinații, tsserver începe să gâfâie serios. Autocomplete-ul din VS Code are un delay vizibil de 1-2 secunde, iar build time-ul pe CI crește. Noi am observat o creștere de ~15% la tsc --noEmit pe un monorepo când am exagerat cu tipurile imbricate.

Regula mea de bun simț: ține template literals pentru combinații finite și logice (sub 300-400 de variante). Dacă ajungi la mii de permisiuni ultra-dinamice, mai bine lași un string simplu și muți validarea strict în runtime cu Zod sau ArkType.

Voi cum gestionați string-urile de RBAC/ABAC între backend și client?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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