eduardweb.
TypeScriptÎncepător#typescript#frontend#webdev

Utility types în practică: Cum scapi de interfețele duplicate în TypeScript

De Liliana Ghiță, 2 iul. 2026 · 16 vizualizări · 2 like-uri

Postat 2 iul. 2026
typescript
interface User {
  id: string;
  name: string;
  email: string;
  role: 'admin' | 'user';
  createdAt: Date;
}

// Pick: luăm doar ce ne trebuie pentru UI
type UserSummary = Pick<User, 'id' | 'name'>;

// Omit: scoatem datele pe care nu vrem să le expunem
type PublicUser = Omit<User, 'role' | 'createdAt'>;

// Partial: ideal pentru operații de update (PATCH)
type UpdateUserPayload = Partial<Omit<User, 'id'>>;

// ReturnType: extragem automat tipul returnat de o funcție
function formatUser(user: User) {
  return {
    label: `${user.name} (${user.email})`,
    value: user.id
  };
}

type FormattedUser = ReturnType<typeof formatUser>;

Salutare! Văd des pe forum juniori (și chiar oameni cu câțiva ani buni în spate) care își complică viața definind zeci de interfețe aproape identice pentru aceeași entitate. Ba una pentru baza de date, ba una pentru request-ul de update, ba alta pentru ce trimiți pe frontend. La un proiect măricel, ajungi să ai un fișier de tipuri de te ia durerea de cap.

Am pățit asta acum vreo trei ani pe o aplicație cu vreo 12k useri activi. Codul devenise atât de greoi încât dacă adăugam un câmp nou în baza de date, trebuia să modific în șase locuri diferite. Atunci am zis gata și am trecut totul pe utility types. Am redus codul duplicat pe partea de tipizare cu aproape 40%.

Pick și Omit: Curățenie în payload-uri

Să zicem că ai o interfață mare pentru User. Are ID, nume, email, rol, dată de creare și poate niște flag-uri interne. Când vrei să afișezi o listă simplă de utilizatori într-un dropdown, n-ai nevoie de toate detaliile.

În loc să scrii o interfață nouă numită UserDropdownItem, folosești Pick<User, 'id' | 'name'>. Îi spui clar compilatorului: „Vreau doar proprietățile astea două”.

La polul opus avem Omit. Îl folosești când vrei să excluzi ceva sensibil. De exemplu, când trimiți datele userului către frontend, vrei să omiți câmpul de parolă sau rolul de admin. Faci un Omit<User, 'passwordHash' | 'isAdmin'> și ai rezolvat problema elegant.

Trade-off sincer: Omit e rapid, dar vine cu un risc la refactoring. Dacă schimbi numele unui câmp din interfața de bază (de exemplu, din passwordHash în password), s-ar putea ca Omit-ul să nu mai dea eroare, dar câmpul nou să treacă neobservat în frontend. Folosiți-l cu atenție.

Partial și Required: Salvarea pentru PATCH-uri

Aici e clasicul caz de update. Când un utilizator își editează profilul, probabil trimite doar câmpurile pe care le-a modificat, nu tot obiectul. Aici intervine Partial<User>, care face ca toate proprietățile să devină opționale (le pune un ? la final).

Dar atenție, Partial e o sabie cu două tăișuri. Dacă ai o funcție care chiar are nevoie de măcar un câmp pentru a funcționa, Partial nu te va proteja dacă trimiți un obiect complet gol {}. Compilatorul va zice că e ok, dar baza de date s-ar putea să plângă.

La polul opus, Required face exact invers: transformă toate proprietățile opționale dintr-o interfață în proprietăți obligatorii. E util când ai configurări default opționale pentru o librărie, dar în interiorul codului tău vrei să fii sigur că toate au valori populate.

ReturnType: Tipizare dinamică fără efort

Pe asta o folosesc cel mai des când lucrez cu factory functions sau când folosesc librării externe care nu își exportă direct tipurile de return.

În loc să scriu de mână structura returnată de o funcție complexă de mapare (care se poate schimba oricând), las TypeScript să își dea seama singur folosind ReturnType<typeof functiaMea>. Dacă funcția își schimbă structura returnată, tipul se updatează automat peste tot în aplicație.

Voi ce utility types folosiți cel mai des în producție și unde vi s-a părut că v-au salvat cel mai mult timp?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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