eduardweb.
TypeScriptÎncepător#typescript#clean-code#web-development

De la cod duplicat la TypeScript curat: Cum folosesc Pick, Omit și ReturnType în producție

De Paul Ene, 5 iul. 2026 · 16 vizualizări · 3 like-uri

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

// 1. Omit & Pick
type UserPublic = Omit<User, 'passwordHash'>;
type UserCredentials = Pick<User, 'email' | 'passwordHash'>;

// 2. Partial
type UserUpdatePayload = Partial<Omit<User, 'id' | 'createdAt'>>;

// 3. ReturnType
function createUserSession(user: UserPublic) {
  return {
    token: "jwt-token-here",
    expiresAt: Date.now() + 3600000,
    user
  };
}
type Session = ReturnType<typeof createUserSession>;

Salut. Văd des în codul scris de juniori (și nu doar de ei) o grămadă de interfețe TypeScript repetate aproape identic. Ai un User, apoi ai UserCreateDto, apoi UserUpdateDto, apoi UserPublicProfile. Schimbi un tip de date în baza de date și trebuie să modifici în șase locuri diferite. E un calvar pe care l-am trăit și eu acum câțiva ani pe un proiect cu peste 15.000 de utilizatori activi, unde refactoring-ul devenise un coșmar birocratic.

Soluția nu e să scrii mai mult cod, ci să folosești utility types. Hai să vedem cum le folosesc eu în viața de zi cu zi ca să îmi păstrez codul DRY (Don't Repeat Yourself) și mintea întreagă.

Pick și Omit: Selecția chirurgicală

Să zicem că ai o entitate mare, un User care vine direct din baza de date. Are ID, email, parolă hash-uită, roluri și timestamps. Când trimiți datele către frontend, evident că nu vrei să trimiți hash-ul parolei.

În loc să creezi o interfață nouă UserPublic, folosești Omit:

type UserPublic = Omit<User, 'passwordHash' | 'salt'>;

Dacă ai nevoie doar de două câmpuri pentru un dropdown de selecție, folosești Pick:

type UserSimple = Pick<User, 'id' | 'email'>;

Trade-off-ul aici e subtil: dacă redenumești un câmp în interfața de bază User, TypeScript te va avertiza imediat. Dar dacă folosești Omit și adaugi un câmp nou, sensibil, în User (să zicem isAdmin), acesta va fi inclus automat în UserPublic dacă nu ești atent să îl adaugi la lista de omiteri. De asta, uneori, Pick e mult mai sigur pentru date publice.

Partial și Required: Pentru formulare și update-uri

Când faci un update prin PATCH, utilizatorul trimite doar câmpurile pe care vrea să le schimbe. Aici intervine Partial, care face toate proprietățile opționale.

type UserUpdateInput = Partial<Omit<User, 'id' | 'createdAt'>>;

Este extrem de util, dar vine cu un cost. Dacă folosești Partial prea des în straturile interne ale aplicației, te trezești că trebuie să pui if-uri de validare peste tot, pentru că TypeScript va crede că orice câmp poate fi undefined la runtime.

Required face exact opusul. Îl folosesc destul de rar, de obicei în teste sau în middleware-uri de validare unde primesc un obiect cu opționale, dar știu sigur că după validare toate câmpurile sunt completate.

ReturnType: Magia din spatele funcțiilor

Când lucrezi cu factory functions sau cu librării externe care nu își exportă direct tipurile de return, ReturnType e aur curat. Am economisit cred că 30% din timpul de scriere a tipurilor la un wrapper de API folosind asta.

Dacă ai o funcție complexă care generează un config, nu are rost să scrii manual tipul acelui config. Lasă TypeScript să îl deducă singur din return-ul funcției.

Concluzia mea

Utility types sunt fantastice, dar au o capcană: lizibilitatea. Dacă începi să le imbrici ca într-un meci de Inception – ceva de genul Partial<Omit<ReturnType<typeof oFunctie>, 'id'>> – colegii tăi de echipă o să te urască la code review. Codul devine greu de citit și IDE-ul va afișa niște erori kilometrice dacă ceva se strică.

Voi cum procedați? Preferați să aveți interfețe explicite scrise cap-coadă pentru fiecare caz de utilizare, sau folosiți utilitarele astea la maximum?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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