type Color = string | { r: number; g: number; b: number };
type Theme = Record<string, Color>;
// 1. Cu satisfies: valideaza structura, dar pastreaza tipul specific (string literal)
const theme = {
primary: "#0070f3",
secondary: { r: 0, g: 0, b: 0 }
} satisfies Theme;
// Metoda de string functioneaza fara eroare de compilare!
theme.primary.toUpperCase();
// 2. Cu anotare clasica: pierzi autocompletion-ul specific
const themeStrict: Theme = {
primary: "#0070f3"
};
// Error: Property 'toUpperCase' does not exist on type 'Color'.
// themeStrict.primary.toUpperCase();Salutare. Am refăcut recent o librărie de config-uri la un proiect cu peste 80k useri lunari unde scăpaseră în producție două bug-uri de runtime din cauza unor casting-uri abuzive cu as. Am curățat vreo 40 de assertion-uri inutile și le-am înlocuit cu satisfies sau anotări directe.
Operatorul satisfies (introdus în TS 4.9) încă le dă bătăi de cap multora. Hai să trecem prin 4 scenarii concrete pe care le folosesc zilnic.
1. Anotarea explicită (const x: Type = ...)
Aceasta este abordarea clasică. O folosești când vrei ca variabila să aibă strict tipul declarat și nu te interesează să păstrezi literalele specifice sau valorile inferate de compiler.
Unde e nasol? În momentul în care ai un union type în interfață (de exemplu string | number), TypeScript va lărgi tipul variabilei la acel union. Pierzi autocompletion-ul specific pentru valoarea pe care tocmai ai scris-o. E perfectă pentru DTO-uri, parametri de funcție sau stare de React unde tipul trebuie să rămână generic pentru mutații ulterioare.
2. Operatorul satisfies pentru validare fără narrowing loss
satisfies este eroul de care aveam nevoie. Verifică dacă obiectul tău respectă o anumită structură (o interfață sau un type), dar lasă compiler-ul să inferenze cel mai specific tip posibil.
Merge brici la obiecte de configurare, routere sau theme tokens. Dacă declari o culoare ca fiind "#0070f3", TypeScript va ști că e exact acel string literal, nu un generic string. În plus, dacă uiți o proprietate obligatorie din interfață, primești eroare la compilare. Câștigi Type Safety fără să pierzi din precizia DX-ului.
3. Type assertion (as Type) — folosește-l doar când arde
Aici e zona periculoasă. Când scrii as CustomType, îi spui efectiv compilatorului să treacă peste verificări și că știi tu mai bine ce faci.
L-am păstrat în codebase doar în două situații: interacțiuni directe cu DOM-ul (unde TS nu are de unde să știe ce element e în HTML) și date venite dintr-un SDK legacy fără tipuri generate. În rest, 90% din cazurile de as pe care le văd la code review sunt doar lene de a tipiza corect un unknown sau un generic.
4. Pattern-ul satisfies + as const pentru config-uri
Combinația asta e aur curat pentru configurații imutabile. Cu as const faci toate proprietățile readonly și le îngheți tipurile la nivel de literală, iar cu satisfies te asiguri că nu ai uitat vreo cheie din schema globală.
Așa am salvat cam 15% din timpul de debugging pe rutele de backend, fiindcă IDE-ul ne oferă acum autocomplete exact pe valorile permise, fără riscul să modificăm accidental config-ul la runtime.
Concluzie
Regula mea simplă: folosește anotări explicite pentru stări mutate sau DTO-uri, satisfies pentru config-uri și date statice, și interzice as cu linter-ul, lăsându-l doar ca ultimă soluție cu comentariu explicativ.
Voi cât de des mai folosiți as în proiectele de zi cu zi?