generics () în TypeScript

Parametri de tip: o funcție sau clasă scrisă o dată, care funcționează cu orice tip și păstrează tipul exact.

Sintaxă
function f<T>(x: T): T { ... }
interface Cutie<T> { valoare: T }
class Stiva<T> { ... }
type Lista<T> = T[];

Ce face generics (<T>)

Un generic este un tip-parametru, scris între paranteze unghiulare: <T>. Așa cum un parametru obișnuit primește o valoare la apel, T primește un tip. function primul<T>(lista: T[]): T | undefined funcționează pentru orice tablou și returnează exact tipul elementelor: pentru string[] dă string, pentru Elev[] dă Elev.

De obicei nu scrii tipul la apel: TypeScript îl deduce din argumente (primul([1, 2]) deduce T = number). Îl poți da și explicit: primul<string>([]). Pot fi mai mulți parametri (<K, V>), iar unul poate avea o valoare implicită (<T = string>).

Generics apar peste tot în bibliotecile standard: Array<T>, Map<K, V>, Promise<T>, Set<T>. Alternativa, any, acceptă tot dar pierde tipul; un generic acceptă tot și îl păstrează. Pentru a cere ca T să aibă anumite proprietăți vezi constrângeri generice.

Exemple

Exemplul 1: Funcție generică

Aceeași funcție returnează number, string sau Elev, după tabloul primit.

function ultimul<T>(lista: T[]): T | undefined {
    return lista[lista.length - 1];
}

interface Elev {
    nume: string;
}

const n = ultimul([9, 10, 8]);                         // number | undefined
const o = ultimul(["Iași", "Cluj"]);                   // string | undefined
const e = ultimul<Elev>([{ nume: "Ana" }, { nume: "Mihai" }]);

console.log(n, o?.toUpperCase(), e?.nume);
console.log(ultimul([]));

Rezultat

8 CLUJ Mihai
undefined

Exemplul 2: Clasă generică

class Stiva<T> {
    #elemente: T[] = [];
    pune(x: T): void {
        this.#elemente.push(x);
    }
    scoate(): T | undefined {
        return this.#elemente.pop();
    }
    get marime(): number {
        return this.#elemente.length;
    }
}

const pagini = new Stiva<string>();
pagini.pune("/acasa");
pagini.pune("/invata");
pagini.pune("/invata/typescript");
console.log(pagini.scoate(), pagini.scoate(), pagini.marime);

Rezultat

/invata/typescript /invata 1

Exemplul 3: Tipul se păstrează, deci greșelile se văd

T este number pentru stiva note, deci un text nu poate fi adăugat.

class Stiva<T> {
    elemente: T[] = [];
    pune(x: T) {
        this.elemente.push(x);
    }
}
const note = new Stiva<number>();
note.pune(10);
note.pune("nouă");

Eroarea afișată de tsc

exemplu.mts(9,11): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.

Greșeli frecvente

  • Folosești un generic doar o dată în semnătură (f<T>(x: T): void). Nu leagă nimic; un tip simplu (unknown) e mai clar.
  • Scrii <T> într-o funcție săgeată dintr-un fișier .tsx; acolo este citit ca tag JSX. Scrie <T,> sau <T extends unknown>.

Comenzi înrudite

  • constrângeri generice (extends): T extends Tip limitează tipurile permise pentru T, ca funcția să poată folosi proprietățile lui Tip.
  • keyof: keyof T dă union-ul numelor de proprietăți ale lui T, de exemplu „nume” | „oras”.
  • funcții cu tipuri: Pui tipuri pe parametrii și pe rezultatul funcțiilor; fiecare apel este verificat la compilare.
  • conditional types: Un tip care alege între două variante după o condiție: T extends U ? X : Y.
  • NoInfer: NoInfer<T> oprește deducerea lui T din acel parametru; T se deduce doar din celelalte argumente.