eduardweb.
TypeScript avansatAvansat#security#typescript#clean-code#webdev

Cum m-au salvat template literal types de la debug în producție pe sistemul de permisiuni

De Teodor Pascu, 19 iun. 2026 · 17 vizualizări · 2 like-uri

Postat 19 iun. 2026
typescript
type Resource = 'users' | 'posts' | 'billing';
type Action = 'create' | 'read' | 'update' | 'delete';

// Generăm automat tipul api:users:create, api:posts:read etc.
type Permission = `api:${Resource}:${Action}`;

interface User {
  id: string;
  permissions: Permission[];
}

function checkPermission(user: User, permission: Permission): boolean {
  return user.permissions.includes(permission);
}

const dev: User = {
  id: 'usr_102',
  permissions: ['api:users:read', 'api:posts:create']
};

// Compiles perfectly
checkPermission(dev, 'api:users:read');

// TS Error: Argument of type '"api:user:write"' is not assignable to parameter of type 'Permission'
// checkPermission(dev, 'api:user:write');

Am avut recent de implementat un sistem de RBAC (Role-Based Access Control) pe un proiect destul de măricel, cu vreo 12 microservicii și sute de rute în frontend. Clasica problemă de care m-am lovit de zeci de ori în carieră: scrie un coleg api:user:write în loc de api:users:write (cu "s") și ne trezim cu bug-uri stupide în producție pentru că backend-ul dădea 403, deși în cod totul părea în regulă la prima vedere.

În loc să lăsăm permisiunile ca simple string-uri sau să facem enum-uri gigantice care trebuie importate peste tot și devin greu de întreținut, am decis să las compilatorul de TypeScript să-și facă treaba. Template literal types sunt ideale exact pentru scenariul ăsta. Practic, poți să concatenezi tipuri de tip string exact cum faci cu template literals în JavaScript la runtime, dar totul se întâmplă în faza de compilare.

Să zicem că avem resursele noastre de bază și acțiunile clasice de CRUD. Definim două tipuri simple de tip union. Când le combinăm într-un template literal, TypeScript generează automat toate permutările posibile. Dacă ai 5 resurse și 4 acțiuni, ai instant 20 de variante valide, verificate la sânge de compilator, fără să scrii cod redundant.

Unde se complică treaba și care e trade-off-ul?

La prima vedere, pare magic. Dar am pățit o chestie pe un proiect mai vechi unde aveam o matrice de permisiuni uriașă. Dacă începi să pui wildcard-uri sau să imbrici template-urile prea adânc (de genul api:${Resource}:${SubResource}:${Action}), compilatorul de TS începe să gâfâie. Am observat o creștere cu vreo 30% a timpului de build pe CI când am exagerat cu combinatorica asta. TypeScript are o limită internă de permutări (în jur de 100.000 de instanțieri de tipuri) înainte să arunce faimoasa eroare "Expression produces a union type that is too complex to represent".

Un alt trade-off sincer este legat de granițele aplicației. La runtime, tipurile astea dispar complet. Dacă primești permisiunile userului de la un API extern ca fiind un simplu array de string-uri (string[]), tot va trebui să scrii un type guard sau o validare cu Zod ca să "convingi" compilatorul că acele date dinamice chiar respectă formatul tău strict. Template-urile te ajută să nu faci greșeli în codul tău, dar nu te scapă de validarea datelor care vin din exterior.

Cum arată în practică

Am scris o funcție utilitară simplă pe care o folosim acum în rutele de React și în middleware-ul de Node.js. E incredibil de satisfăcător să vezi cum autocompletion-ul din IDE îți sugerează exact ce permisiuni ai voie să scrii, eliminând complet ghicitul sau căutatul prin documentația de API.

Voi cum gestionați permisiunile pe proiectele mari? Mergeți pe varianta asta strictă bazată pe tipuri sau preferați validarea flexibilă la runtime și teste de integrare?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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