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

Utility types în TypeScript pe care le folosesc zilnic: Pick, Omit, Partial și ReturnType

De Ana Ionescu, 23 iul. 2026 · 13 vizualizări · 3 like-uri

Postat 23 iul. 2026
typescript
interface User {
  id: string;
  name: string;
  email: string;
  passwordHash: string;
  createdAt: Date;
}

// 1. Omit - DTO public, fără date sensibile
type PublicUser = Omit<User, 'passwordHash'>;

// 2. Partial - Payload pentru endpoint-ul de PATCH
type UpdateUserPayload = Partial<PublicUser>;

// 3. ReturnType - Preluare tip automat din funcție
async function fetchUserProfile(userId: string) {
  return {
    id: userId,
    name: 'Alex',
    email: 'alex@dev.ro',
    roles: ['admin', 'editor']
  };
}

type UserProfileResult = Awaited<ReturnType<typeof fetchUserProfile>>;

Salutare tuturor! Văd extrem de des programatori care duplică interfețe întregi doar ca să scoată sau să modifice două câmpuri pentru un endpoint de API. Am făcut și eu greșeala asta prin 2017 la un proiect cu peste 10.000 de utilizatori zilnici și am ajuns să am 5 variante de User pe care le uitam neactualizate când schimbam schema.

Utilitarele native din TypeScript îți salvează timp și previn bug-urile stupide de refactoring. Hai să vedem cum le aplici direct în producție, fără teorie inutilă.

Partial și Required: Formulare și Patch-uri fără mizerie în cod

Gândește-te la o entitate User din DB. Are id, name, email, createdAt și alte 10 câmpuri. Când faci un endpoint de PATCH /users/:id, clientul trimite doar câmpurile pe care dorește să le schimbe.

Greșeala clasică este să pui opțional ? pe toate câmpurile din interfața principală User. Rezultatul? Peste tot în aplicație TypeScript te va pune să verifici dacă user.email există, deși în baza de date e garantat NOT NULL.

Soluția e să ții interfața User strictă și să folosești Partial<User> pe payload-ul de update. În sens invers, Required<T> e excelent când primești un obiect de configurare cu proprietăți opționale de la utilizator, dar în interiorul modulului tău vrei să le aplici pe toate cu valori implicite.

Pick și Omit: Construiește DTO-uri curate

Pe backend, rareori vrei să trimiți obiectul complet din DB către client. Ai parole hashed, tokens de securitate sau metadata internă.

În loc să creezi un UserResponseDto unde copiezi manual 8 câmpuri, scrii rapid type PublicUser = Omit<User, 'passwordHash' | 'internalNotes'>. Dacă mâine adaugi un câmp nou avatarUrl în modelul User, el va fi inclus automat și în DTO, fără muncă în plus.

Folosesc Pick când am nevoie de un subset foarte mic de date. De exemplu, pentru un dropdown de selectat autori, îmi trebuie doar id și name: type AuthorOption = Pick<User, 'id' | 'name'>.

ReturnType: Extrage tipul fără să te lupți cu librăriile

M-am lovit des de problema asta la integrarea cu SDK-uri terțe (Stripe, Prisma) sau la factory functions. Funcția dintr-o librărie returnează o structură uriașă, dar autorii nu au exportat direct tipul acelei resurse.

În loc să re-creezi tipul prin copy-paste, lași compilatorul să lucreze pentru tine folosind ReturnType<typeof numeFunctie>. Am economisit ore bune de scris interfețe manuale care oricum s-ar fi stricat la următorul upgrade de pachete.

Unde devine nasol (Trade-off-uri)

Utility types sunt grozave, dar creează o capcană imensă de lizibilitate dacă exagerezi. Când vezi în cod ceva de genul Partial<Omit<Pick<User, 'id' | 'email' | 'role'>, 'role'>>, e clar că s-a sărit calul.

Dacă un tip combinat devine mai greu de descifrat decât dacă l-ai fi scris manual cu 3 proprietăți, oprește-te. Codul este citit mult mai des decât este scris. Eu am o regulă simplă pe proiecte: maxim două utilitare combinate pe o singură linie. Peste asta, creează un tip intermediar clar denumit.

Voi ce utilitare native folosiți cel mai des în aplicații? Aveți vreo regulă strictă în echipă împotriva combinărilor exagerate de tipuri?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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