Predicate de Tip Personalizate și Funcții de Aserțiune (is & asserts) în TypeScript

Narrowing și type guards TypeScript 3.7+

Ghid complet pentru User-Defined Type Guards și Assertion Functions în TypeScript: transferul logicii de validare către tsc folosind clauzele is și asserts.

Sintaxă
function esteUser(x: unknown): x is User; function afirmaString(x: unknown): asserts x is string;

Atunci când primiți date dintr-o sursă externă nesigură (un răspuns de API fetch(), un fișier JSON de pe disc sau un payload de webhook), tipul inițial este unknown. O simplă funcție booleană clasică (function valida(x) { return true; }) verifică datele la runtime, dar nu informează compilatorul TypeScript despre noul tip al variabilei. Aici intervin Predicatele de Tip (Type Predicates) și Funcțiile de Aserțiune (Assertion Functions).

1. Predicate de Tip (User-Defined Type Guards cu clauza is)
interface Administrator {
    id: number;
    rol: 'admin';
    drepturiComplete: boolean;
}

// Funcție cu predicat de tip: 'val is Administrator' în loc de 'boolean'
function esteAdministrator(val: unknown): val is Administrator {
    return (
        typeof val === 'object' &&
        val !== null &&
        'rol' in val &&
        (val as any).rol === 'admin'
    );
}

function proceseazaCont(cont: unknown) {
    if (esteAdministrator(cont)) {
        // În interiorul acestui bloc IF, TypeScript știe 100% că 'cont' este Administrator!
        console.log(`Admin logat cu drepturi: ${cont.drepturiComplete}`);
    } else {
        console.log("Contul nu este de tip administrator.");
    }
}

2. Funcții de Aserțiune (Assertion Functions cu clauza asserts)

Introduse în TypeScript 3.7, funcțiile de aserțiune verifică o condiție și, în caz de eșec, aruncă o eroare (throw exception). Dacă funcția nu aruncă eroare, compilatorul restrânge automat tipul variabilei pentru restul scope-ului funcției apelante, fără a mai necesita un bloc if (...) suplimentar:

Eliminarea blocurilor IF repetitive cu asserts
// Funcția garantează că valoarea nu este null sau undefined:
function afirmaNonNullable<T>(val: T, mesaj?: string): asserts val is NonNullable<T> {
    if (val === null || val === undefined) {
        throw new Error(mesaj || "Valoarea nu poate fi null sau undefined!");
    }
}

function trimiteNotificare(email: string | null) {
    // email este string | null
    afirmaNonNullable(email, "Email-ul lipsește din sesiune!");
    
    // DIN ACEST PUNCT, email este garantat exclusiv 'string':
    console.log(`Trimis către: ${email.toLowerCase()}`);
}

Filtrarea Curată a Array-urilor cu Type Predicates

Un caz clasic de utilizare este filtrarea elementelor null sau undefined dintr-un array. Fără un predicat de tip, array.filter(Boolean) returnează în continuare (T | null)[]:

const lista: (string | null)[] = ['București', null, 'Cluj', null];

// PREDICAT DE TIP ÎN CALLBACK:
const oraseValide = lista.filter((oras): oras is string => oras !== null);
// oraseValide este inferat corect ca: string[] (fără niciun null!)