eduardweb.
TypeScriptÎncepător#typescript#frontend#clean-code#backend

De ce scrii TypeScript de mână când ai Utility Types? Pick, Omit și ReturnType în producție

De Corina Dobre, 27 iun. 2026 · 19 vizualizări · 3 like-uri

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

// Folosim Pick pentru a defini datele de înregistrare
type RegisterInput = Pick<User, 'name' | 'email'> & { passwordHash: string };

// Folosim Omit pentru a trimite profilul către frontend (fără date sensibile)
type PublicProfile = Omit<User, 'role' | 'createdAt'>;

// Folosim Partial pentru update (patch)
type UpdateInput = Partial<Pick<User, 'name' | 'email'>>;

// ReturnType extras direct dintr-o funcție
function buildUserSession(user: User) {
  return {
    token: "jwt_token_here",
    expiresAt: Date.now() + 3600,
    user: { id: user.id, name: user.name }
  };
}

type SessionData = ReturnType<typeof buildUserSession>;

Am văzut prea des cod TypeScript unde oamenii duplică interfețe întregi doar pentru că au nevoie de o versiune "fără ID" pentru crearea unei resurse sau de o versiune "cu toate câmpurile opționale" pentru un update. E o pierdere de timp și o sursă garantată de bug-uri la refactoring. În postarea asta îți arăt cum folosesc eu utilitarele astea ca să țin codul curat și DRY.

De la copy-paste la Pick și Omit

Am avut cazul acum un an, la un proiect cu vreo 14k utilizatori activi, unde aveam o entitate User destul de stufoasă, cu vreo 25 de proprietăți. Pentru formularul de înregistrare aveam nevoie doar de email, parolă și nume. Pentru update-ul de profil, doar de nume și avatar.

La început, echipa crease câte o interfață separată pentru fiecare caz: UserRegisterDTO, UserUpdateDTO etc. Când am schimbat tipul câmpului status din string în enum, jumătate din interfețele astea au rămas neactualizate. Ne-am trezit cu erori de validare direct în producție fiindcă tipurile din frontend nu mai pușcau cu cele din backend.

Aici intervin Pick și Omit. Cu Pick selectezi exact ce vrei, iar cu Omit arunci ce nu ai nevoie.

Trade-off-ul sincer: Omit e extrem de comod, dar vine cu un risc la refactoring. Dacă adaugi un câmp sensibil în interfața de bază (să zicem, isAdmin), și folosești Omit<User, 'id'>, noul câmp isAdmin va fi inclus automat în tipul rezultat. De asta, pentru chestii de siguranță sau API-uri publice, prefer Pick. E mult mai safe să folosești whitelist în loc de blacklist.

Cum te salvează Partial și Required

Partial<T> e probabil cel mai folosit utilitar. Face toate proprietățile opționale. Îl folosesc aproape exclusiv pe rutele de PATCH din API sau când scriu teste unitare și am nevoie de un mock rapid pentru un obiect complex, dar mă interesează doar 2-3 proprietăți pentru testul respectiv.

La polul opus avem Required<T>, care face exact invers: transformă tot ce e opțional în obligatoriu. Am pățit să-l folosesc la sisteme de configurare. Userul trimite un obiect de config unde toate câmpurile sunt opționale (are fallback-uri), dar în interiorul modulului meu am nevoie de garanția că toate cheile sunt populate după ce am aplicat valorile default.

ReturnType: lasă compilatorul să muncească

ReturnType<T> e salvator când ai de-a face cu fabrici de funcții sau când vrei să extragi tipul returnat de o funcție dintr-o librărie terță care nu și-a exportat tipurile cum trebuie. Decât să stai să reconstruiești manual tipul returnat (și să riști să-ți scape ceva la update-uri de librărie), mai bine pui TypeScript să îl extragă direct din definiția funcției folosind typeof.

La final, codul devine mult mai robust. Schimbi o singură dată interfața principală în baza de date sau în schema API-ului și tot restul codului se aliniază automat la compilare.

Voi ce folosiți cel mai des? Tot clasicul Partial pentru patch-uri sau v-ați lovit de limitările lui Omit la refactoring masiv?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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