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

Cum să nu mai scrii interfețe duplicate: Utility Types în producție

De Ioana Marinescu, 12 iun. 2026 · 19 vizualizări · 2 like-uri

Postat 12 iun. 2026
typescript
interface User {
  id: string;
  email: string;
  passwordHash: string;
  phoneNumber?: string;
  createdAt: Date;
}

// Folosim Pick pentru profilul public expus în API
type PublicProfile = Pick<User, 'id' | 'email'>;

// Folosim Omit și Partial pentru a permite update-uri parțiale, dar sigure
type UpdatePayload = Partial<Omit<User, 'id' | 'passwordHash' | 'createdAt'>>;

// Validăm că avem numărul de telefon completat la checkout
type CheckoutContact = Required<Pick<User, 'email' | 'phoneNumber'>>;

function createUserSession(user: PublicProfile) {
  return {
    token: "jwt-token-here",
    user,
    activeAt: new Date()
  };
}

// ReturnType deduce automat structura returnată de funcție
type SessionInfo = ReturnType<typeof createUserSession>;

Salutare! Ne lovim zilnic de duplicarea tipurilor în TypeScript pentru că ne e lene sau pur și simplu ne grăbim să livrăm un feature rapid. Am făcut un audit acum câteva luni pe o aplicație cu vreo 12.000 de utilizatori activi și am găsit nu mai puțin de 6 interfețe diferite pentru entitatea de User. Toate scrise de mână, cu mici variații care creau bug-uri la fiecare deployment.

Soluția nu e să scrii mai mult cod, ci să folosești ce îți dă deja limbajul. Hai să vedem cum curățăm mizeria asta folosind utilitarele native.

Pick și Omit: Cum decupezi exact ce ai nevoie

Cel mai des am pățit asta la fluxul de login și profil public. În baza de date ai un user complet cu id, email, passwordHash, createdAt și roles. În frontend, când afișezi un avatar simplu sau o listă de membri, ai nevoie doar de id și email.

În loc să creezi o interfață nouă numită PublicUser pe care sigur o să uiți să o actualizezi când adaugi câmpuri noi în baza de date, mai bine folosești Pick.

La fel și cu Omit. Când trimiți datele de profil către un API de update, vrei să permiți modificarea tuturor câmpurilor, mai puțin a ID-ului și a parolei. Omit e perfect aici pentru că exclude exact ce nu vrei să fie atins de utilizator.

Există totuși un trade-off sincer la faza asta. Dacă faci refactoring la baza de date și schimbi numele unui câmp, Pick și Omit vor crăpa direct la compilare, ceea ce e super bine. Însă, dacă abuzezi de ele pe lanțuri lungi de dependențe, devine foarte greu de urmărit de unde provine de fapt o proprietate când faci debug rapid în IDE.

Partial și Required: Update-uri și Validări

Partial transformă toate proprietățile unui tip în unele opționale. Este utilitarul perfect pentru request-urile de tip PATCH. Când utilizatorul își actualizează doar prenumele în setări, API-ul tău primește doar acea bucățică din obiect, nu tot profilul.

Dar atenție mare în practică. Partial te scapă de boilerplate, dar te poate lăsa descoperit în runtime. Dacă pui Partial peste tot doar ca să scapi de erorile de compilare din teste sau mock-uri, te vei trezi cu erori de tip Cannot read property of undefined pentru că ai presupus greșit că un câmp e mereu acolo.

La polul opus avem Required. Pe ăsta l-am folosit mult la validarea formularelor de checkout. Chiar dacă în baza de date unele câmpuri sunt opționale (cum ar fi phoneNumber), în pasul final de plată am nevoie ca toate să fie completate. În loc să re-declar interfața, aplic Required pe tipul existent și TypeScript mă obligă să am toate valorile populate înainte de a trimite comanda spre procesare.

ReturnType: Când nu vrei să ghicești ce returnează o funcție

Pe ReturnType l-am subestimat ani de zile, până când am început să lucrez intensiv cu fabrici de funcții (factory pattern) și configuratoare dinamice.

Dacă ai o funcție complexă care generează un obiect de configurare, tiparea manuală a acelui return e un coșmar la fiecare modificare. Dacă funcția se schimbă, trebuie să modifici manual și interfața de return. Cu ReturnType<typeof myFunction>, TypeScript deduce automat structura exactă. Am economisit lejer 30% din timpul de mentenanță pe un modul de raportare financiară doar prin simplul fapt că am lăsat compilatorul să-și facă treaba singur.

Până la urmă, scrierea manuală de tipuri duplicate e doar o formă de datorie tehnică pe care o plătești mai târziu cu dobândă.

Voi cum procedați în proiectele voastre? Preferați să scrieți interfețe separate pentru fiecare endpoint sau folosiți utilitarele native până când codul devine prea abstract pentru un junior?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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