predicat de tip (is) în TypeScript

Narrowing și type guards TypeScript 1.6

O funcție cu rezultatul x is Tip spune compilatorului că, dacă returnează true, x are tipul Tip.

Sintaxă
function esteTip(x: unknown): x is Tip {
    return /* verificare */;
}

Ce face predicat de tip (is)

Un predicat de tip este o funcție care returnează boolean, dar cu tipul rezultatului scris x is Tip, unde x este numele unui parametru. Când funcția returnează true, TypeScript restrânge tipul argumentului la Tip; când returnează false, îl exclude.

Așa îți scrii propriile type guards pentru interfețe și pentru verificări complicate pe care TypeScript nu le poate deduce singur. Predicatele merg direct în filter: lista.filter(esteElev) are tipul Elev[]. Din TypeScript 5.5, funcțiile simple ca x => x !== undefined sunt recunoscute automat ca predicate.

TypeScript nu verifică dacă corpul funcției spune adevărul. Dacă predicatul returnează true pentru valori greșite, compilatorul te crede și eroarea apare la rulare. Scrie verificarea cu grijă. Pentru metode există și forma this is Tip.

Exemple

Exemplul 1: Validarea datelor primite

interface Produs {
    nume: string;
    pret: number;
}

function esteProdus(x: unknown): x is Produs {
    if (typeof x !== "object" || x === null) return false;
    const p = x as Record<string, unknown>;
    return typeof p.nume === "string" && typeof p.pret === "number" && p.pret >= 0;
}

const raspuns: unknown = JSON.parse('[{"nume":"Pâine","pret":5},{"nume":"Lapte"},{"nume":"Ouă","pret":12}]');
if (Array.isArray(raspuns)) {
    const produse = raspuns.filter(esteProdus);
    const total = produse.reduce((s, p) => s + p.pret, 0);
    console.log(produse.map((p) => p.nume), `${total} lei`);
}

Rezultat

[ 'Pâine', 'Ouă' ] 17 lei

Exemplul 2: Eliminarea valorilor lipsă din tablou

Fără predicat, filter ar păstra tipul (string | undefined)[]. Cu TypeScript 5.5+, predicatul este dedus și din funcția săgeată.

function exista<T>(x: T | undefined | null): x is T {
    return x !== undefined && x !== null;
}

const emailuri = ["ana@scoala.ro", undefined, "mihai@scoala.ro", null];
const valide = emailuri.filter(exista);                  // string[]
const valide2 = emailuri.filter((x) => x != null);       // string[] (dedus)
console.log(valide.map((e) => e.toUpperCase()));
console.log(valide2.length);

Rezultat

[ 'ANA@SCOALA.RO', 'MIHAI@SCOALA.RO' ]
2

Exemplul 3: Predicat care minte

Funcția returnează mereu true. tsc o crede, iar programul se oprește la rulare.

interface Elev {
    nume: string;
}
function esteElev(_x: unknown): _x is Elev {
    return true;      // greșit: nu verifică nimic
}

const x: unknown = 42;
try {
    if (esteElev(x)) console.log(x.nume.toUpperCase());
} catch (e) {
    console.log("Eroare la rulare:", (e as Error).message);
}

Rezultat

Eroare la rulare: Cannot read properties of undefined (reading 'toUpperCase')

Greșeli frecvente

  • Returnezi boolean în loc de x is Tip. Funcția verifică, dar TypeScript nu află nimic din rezultat.
  • Scrii predicatul cu alt nume decât parametrul (function f(a: unknown): b is Elev). tsc dă Cannot find parameter ‘b’.

Comenzi înrudite

  • type guards: Verificări la rulare pe care TypeScript le înțelege și după care știe tipul exact al unei valori.
  • asserts: O funcție asserts aruncă o eroare dacă verificarea eșuează; după apel, TypeScript știe că tipul este cel cerut.
  • narrowing: TypeScript urmărește verificările din cod (if, switch, return) și restrânge tipul unei variabile în fiecare ramură.
  • unknown: Tipul sigur pentru valori necunoscute: acceptă orice, dar nu poți folosi valoarea până nu verifici ce tip are.
  • in guard: „cheie” in x verifică dacă obiectul are proprietatea și restrânge union-ul la variantele care o au.