type Theme = Record<string, string | number[]>;
// 1. Cu adnotare explicită - pierdem tipul exact (eroare la toUpperCase)
const badTheme: Theme = {
primary: '#ff0000',
secondary: [0, 120, 255]
};
// badTheme.primary.toUpperCase(); // Eroare: Property 'toUpperCase' does not exist on type 'string | number[]'
// 2. Cu satisfies - validăm structura, dar păstrăm tipul exact (funcționează!)
const goodTheme = {
primary: '#ff0000',
secondary: [0, 120, 255]
} satisfies Theme;
goodTheme.primary.toUpperCase(); // Funcționează perfectM-am lovit recent de un bug stupid pe un proiect cu vreo 12k utilizatori activi. Un coleg a folosit as ca să scape rapid de o eroare de compilare. Evident, tipul real de la runtime era altul și ne-am trezit cu crash în producție. De atunci, am stabilit o regulă destul de clară în echipă legată de cum împărțim teritoriul între adnotările clasice, aserțiuni și operatorul satisfies introdus în TS 4.9.
Hai să trecem prin cele 4 cazuri concrete pe care le întâlnești zilnic în cod.
Cazul 1: Configurații și constante cu valori precise
Aici satisfies este rege absolut. Să zicem că ai un obiect de configurare pentru teme, unde culorile pot fi string-uri simple sau array-uri RGB.
Dacă pui o adnotare explicită clasică (ex: : Record<string, string | number[]>), pierzi autocompletion-ul specific. TypeScript va crede că oricare cheie poate fi fie string, fie array.
Dacă folosești satisfies, compilerul validează că obiectul respectă structura generală, dar păstrează tipurile exacte ale valorilor tale. Știe exact că primary e string, deci poți folosi metode de string pe el fără type casting.
Cazul 2: Date venite din API sau JSON.parse
Când primești date din exterior, nu folosi as direct pe variabilă. Este practic o minciună pe care o spui compilerului: „crede-mă pe cuvânt, știu eu ce fac”.
Trade-off-ul sincer aici? Dacă backend-ul schimbă structura, TypeScript nu te va ajuta cu nimic la runtime. Pentru granițele aplicației, adnotările explicite combinate cu o librărie de validare (cum e Zod) sunt sfinte. Folosește as doar dacă ești absolut blocat și n-ai timp de refactoring, dar asumă-ți riscul.
Cazul 3: Return-ul funcțiilor din modulele exportate
Pentru funcțiile publice din serviciile tale, mergi întotdeauna pe adnotări explicite pentru tipul returnat.
Am pățit de multe ori ca, în urma unui refactoring minor în interiorul unei funcții, tipul inferat automat de TypeScript să se schimbe subtil. Dacă nu ai adnotare explicită pe return, eroarea nu apare în funcția modificată, ci în alte 10 fișiere care o consumă. Adnotarea explicită acționează ca un contract rigid care te protejează de propriile greșeli.
Cazul 4: Lucrul cu DOM-ul sau librării terțe prost tipizate
Aici as (Type Assertion) este răul necesar. Când folosești document.getElementById('canvas'), TypeScript știe doar că returnează un HTMLElement generic.
Nu ai cum să folosești satisfies aici pentru că elementul returnat nu este un literal pe care îl controlezi tu. Singura variantă curată este să folosești as HTMLCanvasElement ca să poți accesa contextul 2D.
Care este concluzia?
- Adnotările explicite (
const x: Type) sunt excelente pentru granițe de module, contracte API și funcții exportate. Sunt rigide, dar sigure. - Operatorul
satisfiese genial pentru DX și configurări locale. Îți oferă siguranță fără să-ți distrugă inferența specifică. - Aserțiunea
astrebuie privită ca un bypass de siguranță. Folosește-o doar pentru DOM sau când tipurile dintr-o librărie externă sunt complet sparte.
În echipa voastră cum gestionați asta? Mai folosiți as la ordinea zilei sau ați trecut la satisfies pentru tot ce înseamnă configuri și rute?