Tipuri Condiționale și Cuvântul Cheie infer în TypeScript în TypeScript

Ghid complet pentru Conditional Types și operatorul infer în TypeScript: scrierea de logică decizională la nivel de tipuri și extragerea dinamică a tipurilor din Promise, Array sau funcții.

Sintaxă
type UnwrapPromise<T> = T extends Promise<infer R> ? R : T;

Sistemul de tipuri din TypeScript este Turing-complete, permițând crearea de algoritmi complecși direct la nivelul compilării. La baza acestei expresivități stau Tipurile Condiționale (Conditional Types) combinate cu operatorul infer. Un tip condițional selectează unul dintre două tipuri posibile pe baza unei relații de subtipare, folosind o sintaxă inspirată din operatorul ternar JavaScript (T extends U ? X : Y).

Sintaxa de bază și folosirea cuvântului cheie infer
// Cuvântul cheie 'infer' declară o variabilă de tip temporară ce este dedusă automat:
type ExtrageElementArray<T> = T extends (infer Element)[] ? Element : T;

type Test1 = ExtrageElementArray<string[]>;  // string
type Test2 = ExtrageElementArray<number[]>;  // number
type Test3 = ExtrageElementArray<boolean>;   // boolean (nu este array, returnează T)

// Exemplu clasic: Desfășurarea unui Promise (Unwrap / Awaited):
type DesfasoaraPromise<T> = T extends Promise<infer Rezultat> ? DesfasoaraPromise<Rezultat> : T;

type RaspunsApi = DesfasoaraPromise<Promise<Promise<{ id: number; titlu: string }>>>;
// RaspunsApi este: { id: number; titlu: string }

Extragerea Parametrilor și a Tipului Returnat din Funcții

În dezvoltarea de middleware-uri, handlere sau wrapper-e de evenimente, de multe ori avem nevoie să extragem tipul primului argument al unei funcții externe fără a avea acces direct la interfața exportată:

Extragerea tipului primului argument dintr-o funcție
type PrimulParametru<T> = T extends (primul: infer P, ...rest: any[]) => any ? P : never;

function inregistreazaUser(date: { email: string; varsta: number }, optiuni?: { trimiteEmail: boolean }) {
    // ...
}

// Extragem tipul parametrului 'date':
type DateUser = PrimulParametru<typeof inregistreazaUser>;
// DateUser este: { email: string; varsta: number }

Distributivitatea Tipurilor Condiționale (Distributive Conditional Types)

Când un parametru de tip generic este o uniune (union) și apare neîmpachetat în partea stângă a lui extends, tipul condițional se distribuie automat peste fiecare membru al uniunii:

type FaraNull<T> = T extends null | undefined ? never : T;

type TipCuratat = FaraNull<string | number | null | undefined>;
// Compilatorul evaluează:
// (string extends null|undefined ? never : string) |
// (number extends null|undefined ? never : number) |
// (null extends null|undefined ? never : null)
// Rezultat final: string | number (deoarece 'never' este eliminat automat din uniuni!)

Pentru a preveni comportamentul distributiv (dacă doriți să verificați uniunea ca pe un întreg), împachetați ambele părți în paranteze drepte: [T] extends [null | undefined] ? X : Y.