never în TypeScript

Tipuri de bază TypeScript 2.0

Tipul fără nicio valoare: pentru funcții care nu se termină normal și pentru cazurile imposibile din cod.

Sintaxă
function f(): never { throw new Error("..."); }
const x: never = valoare;   // doar dacă valoare este never

Ce face never

never este tipul gol: nicio valoare nu are tipul never. Îl întâlnești în două situații. Prima: tipul returnat de o funcție care nu se termină niciodată normal, pentru că aruncă mereu o eroare sau intră într-o buclă infinită.

A doua: după ce ai verificat toate variantele unui union, ce rămâne este never. Asta permite verificarea exhaustivă: pui în ramura default a unui switch o atribuire la never. Dacă adaugi mai târziu o variantă nouă în union și uiți să o tratezi, tsc dă eroare exact acolo.

În tipurile condiționale și în mapped types, never înseamnă „elimină”: Exclude și Omit folosesc această regulă. Un union cu never rămâne neschimbat: string | never este string.

Exemple

Exemplul 1: Funcție care aruncă mereu

După apelul esec(), TypeScript știe că restul codului nu se mai execută.

function esec(mesaj: string): never {
    throw new Error(mesaj);
}

function nota(text: string): number {
    const n = Number(text);
    if (n >= 1 && n <= 10) return n;
    return esec(`Notă invalidă: ${text}`);
}

console.log(nota("9"));
try {
    nota("12");
} catch (e) {
    console.log((e as Error).message);
}

Rezultat

9
Notă invalidă: 12

Exemplul 2: Verificare exhaustivă cu never

Fiecare case elimină o variantă. În default nu mai rămâne nimic, deci forma are tipul never.

type Forma =
    | { tip: "cerc"; raza: number }
    | { tip: "patrat"; latura: number };

function arie(forma: Forma): number {
    switch (forma.tip) {
        case "cerc":
            return Math.PI * forma.raza ** 2;
        case "patrat":
            return forma.latura ** 2;
        default: {
            const imposibil: never = forma;
            return imposibil;
        }
    }
}

console.log(arie({ tip: "cerc", raza: 1 }).toFixed(2));
console.log(arie({ tip: "patrat", latura: 3 }));

Rezultat

3.14
9

Exemplul 3: Variantă nouă, netratată

S-a adăugat triunghi în union, dar nu și în switch. tsc arată exact ce caz lipsește.

type Forma =
    | { tip: "cerc"; raza: number }
    | { tip: "patrat"; latura: number }
    | { tip: "triunghi"; baza: number; inaltime: number };

function arie(forma: Forma): number {
    switch (forma.tip) {
        case "cerc":
            return Math.PI * forma.raza ** 2;
        case "patrat":
            return forma.latura ** 2;
        default: {
            const imposibil: never = forma;
            return imposibil;
        }
    }
}

console.log(arie({ tip: "cerc", raza: 1 }));

Eroarea afișată de tsc

exemplu.mts(13,19): error TS2322: Type '{ tip: "triunghi"; baza: number; inaltime: number; }' is not assignable to type 'never'.

Greșeli frecvente

  • Confunzi never cu void. O funcție void se termină normal, dar nu returnează nimic util; una never nu se termină normal deloc.
  • Te miri că un tablou gol declarat fără tip are elemente never[] în anumite contexte. Scrie tipul elementelor: const lista: string[] = [].

Comenzi înrudite

  • void: Tipul returnat de funcțiile care nu întorc nicio valoare utilă, de exemplu cele care doar afișează ceva.
  • discriminated unions: Un union de obiecte cu o proprietate comună de tip literal; verificarea ei arată exact ce variantă ai.
  • unknown: Tipul sigur pentru valori necunoscute: acceptă orice, dar nu poți folosi valoarea până nu verifici ce tip are.
  • conditional types: Un tip care alege între două variante după o condiție: T extends U ? X : Y.
  • Exclude: Exclude<T, U> elimină din union-ul T variantele care se potrivesc cu U.