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

Cum am scăpat de interfețe duplicate în TypeScript cu 5 Utility Types

De Cristian Barbu, 8 oct. 2026 · 9 vizualizări · 2 like-uri

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

// 1. Pick pentru date publice (sigur, stil whitelist)
type PublicUserProfile = Pick<User, 'id' | 'name' | 'email'>;

// 2. Partial pentru payload de update (PATCH)
type UpdateUserPayload = Partial<Pick<User, 'name' | 'email'>>;

// 3. ReturnType pe un factory function dintr-o librărie
function makeDatabaseConnection() {
  return { host: 'localhost', port: 5432, pool: { min: 2, max: 10 } };
}

type DbConfig = ReturnType<typeof makeDatabaseConnection>;

Acum vreo doi ani am intrat pe un proiect de fintech cu vreo 14k linii de TypeScript unde am găsit șapte tipuri diferite pentru aceeași entitate de utilizator. Aveam User, CreateUserDto, UpdateUserPayload, UserSummary, AdminUserView și încă vreo două derivate obscure, toate scrise de mână cu copy-paste. Când s-a adăugat câmpul de twoFactorEnabled, s-a bușit producția fiindcă vreo trei interfețe fuseseră uitate neactualizate.

TypeScript vine din fabrică cu câteva unelte care rezolvă exact problema asta: utility types. Nu e nevoie să le memorezi pe toate 20+, dar dacă stăpânești 5 dintre ele, elimini lejer 40% din codul boilerplate.

Pick și Omit: whitelist vs blacklist

Când vrei o sub-mulțime dintr-o interfață mare, ai două abordări opuse.

Pick<T, Keys> ia doar proprietățile pe care le declari explicit. Dacă ai un User stocat în baza de date cu 20 de coloane și vrei să trimiți pe un tabel în frontend doar câteva detalii, Pick<User, 'id' | 'email' | 'name'> te ține în siguranță. E ca un whitelist.

Omit<T, Keys> face exact inversul: păstrează tot, mai puțin cheile specificate. La prima vedere e comod să zici Omit<User, 'passwordHash' | 'salt'> când returnezi userul pe API.

Aici apare un trade-off pe care mulți îl ignoră: Omit este periculos la refactoring. Dacă adaugi ulterior în User o proprietate sensibilă, cum ar fi ssnToken, Omit o va lăsa să treacă mai departe automat dacă uiți să o treci manual în blacklist. Personal prefer Pick pentru DTO-uri externe tocmai din motive de securitate.

Partial și Required: flexibilitate vs certitudine

Pe endpoint-urile de PATCH nu trimiți niciodată tot obiectul, ci doar câmpurile care s-au modificat. În loc să rescrii tipul cu ? la fiecare rând, folosești Partial<T>. Face toate proprietățile opționale dintr-un foc.

Capcana clasică? Partial este shallow. Dacă ai obiecte imbricate (nested), proprietățile din interior rămân neschimbate. Pentru structuri adânci ai nevoie de un DeepPartial scris de mână sau luat dintr-o librărie precum utility-types.

La polul opus avem Required<T>. Îl folosesc des la funcțiile interne de configurare. Ai o interfață cu opțiuni configurabile unde majoritatea sunt opționale pentru cel ce apelează funcția, dar în interior ai un set de valori default. Cu Required<AppConfig> te asiguri că funcția internă nu verifică la infinit dacă config.timeout este undefined.

ReturnType: când librăria nu vrea să colaboreze

Cea mai enervantă situație: folosești un pachet npm extern, chemi o funcție complexă sau un factory, dar autorul nu a exportat interfața a ceea ce returnează funcția respectivă.

În loc să deduci tipul manual și să-ți bați capul, îi ceri compilatorului să extragă tipul rezultat cu ReturnType<typeof numeFunctie>. Economisești timp enorm când faci wrapper-e peste clienți de Redis, instanțe de Knex sau hook-uri de React.

Voi ce folosiți mai des în proiectele voastre de zi cu zi, Pick sau Omit?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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