Treci la conținutul principal

Inferența tipurilor în TypeScript

Inferența tipurilor înseamnă că TypeScript deduce singur un tip, din valoarea pe care o atribui sau din locul în care este folosită o expresie. Obții verificarea tipurilor fără să scrii de mână fiecare tip.


Cum funcționează inferența tipurilor în TypeScript

Inferența tipurilor este capacitatea TypeScript de a găsi și de a atribui singur tipuri variabilelor, rezultatelor funcțiilor și expresiilor, pe baza modului în care sunt folosite, fără să scrii adnotări explicite de tip.

Astfel codul rămâne scurt și, în același timp, sigur din punctul de vedere al tipurilor.

Concepte cheie:

  • Inferența tipurilor: tipul este detectat automat din valoarea atribuită
  • Tipare contextuală: tipul este dedus din codul înconjurător
  • Cel mai bun tip comun: regula folosită de TypeScript pentru a găsi un singur tip care se potrivește mai multor valori (de exemplu într-un tablou)
  • Lărgire / restrângere: tipurile devin mai largi sau mai precise, în funcție de modul în care este folosită o valoare
  • Când se întâmplă: la inițializarea variabilelor, la valorile returnate, la parametrii impliciți, la callback-uri și la literali

Exemplu

Niciuna dintre aceste variabile nu are o adnotare de tip, totuși fiecare primește un tip precis din valoarea ei. Pentru că name este un string, toUpperCase() funcționează, iar toFixed() ar fi o eroare.

Prompt
Arată cum TypeScript deduce tipurile variabilelor din valorile lor inițiale, fără nicio adnotare de tip. Declară name = "Alice", age = 30, isActive = true, numbers = [1, 2, 3] și mixed = [1, "two", true] cu let, notând în comentarii tipul dedus al fiecăreia (string, number, boolean, number[], (string | number | boolean)[]). Apelează name.toUpperCase() și age.toFixed(2) pentru a arăta că tipurile deduse pot fi folosite și lasă name.toFixed(2) comentat, cu o notă că ar fi o eroare. Nu se afișează nimic.
Ieșire: program TypeScript și rezultatul capturat

Inferența tipului returnat de funcții

TypeScript poate deduce tipul returnat de o funcție analizând instrucțiunile ei return. Dacă ramuri diferite returnează tipuri diferite, rezultatul este o uniune a acestora.

Exemplu

Se deduce că greet returnează string, că add returnează number și că getValue returnează string | number, pentru că cele două ramuri ale ei returnează tipuri diferite.

Prompt
Arată cum TypeScript deduce tipul returnat de o funcție din instrucțiunile ei return. Scrie greet(name: string) care returnează un șir de tip template (dedus ca string), add(a: number, b: number) care returnează suma lor (dedus ca number) și getValue(key: string) care returnează "Alice" când cheia este "name" și 42 în rest (dedus ca string | number). Pune rezultatele apelurilor greet("Bob"), add(5, 3) și getValue("age") în variabile, cu comentarii care numesc tipurile deduse. Nu se afișează nimic.
Ieșire: program TypeScript și rezultatul capturat

Tipare contextuală

TypeScript poate deduce un tip și din contextul în care apare o expresie. Un callback transmis unei metode de tablou sau unui ascultător de evenimente își primește tipurile parametrilor de la acea metodă, așa că nu trebuie să le adnotezi.

Exemplu

Parametrul name din forEach și map este dedus ca string, pentru că names este un string[], iar nameLengths devine number[]. În gestionarul de clic, event este dedus ca MouseEvent. Ascultătorul este împachetat într-o verificare pentru document, ca fișierul să ruleze și în afara browserului.

Prompt
Arată tiparea contextuală: tipurile parametrilor unui callback sunt deduse din locul în care este folosit callback-ul. Creează un tablou names cu "Alice", "Bob" și "Charlie". În names.forEach afișează fiecare nume scris cu majuscule (parametrul name este dedus ca string), apoi construiește nameLengths cu names.map, returnând lungimea fiecărui nume (dedus number[]). La final înregistrează un gestionar document.addEventListener("click", ...) care afișează event.clientX și event.clientY (parametrul event este dedus ca MouseEvent), protejat de o verificare typeof document !== "undefined", ca fișierul să ruleze și în afara browserului.
Ieșire: program TypeScript și rezultatul capturat

Inferența tipurilor la obiectele literale

Când creezi un obiect literal, TypeScript deduce tipul fiecărei proprietăți, inclusiv al obiectelor imbricate. Accesarea unei proprietăți care nu există este depistată la compilare.

Exemplu

Obiectul user primește o formă dedusă completă, așa că se știe că user.details.address.city este un string. Liniile comentate arată accesări pe care TypeScript le-ar respinge.

Prompt
Arată că TypeScript deduce forma completă a unui obiect literal, inclusiv obiectele imbricate. Creează o constantă user cu id: 1, name: "Alice", email: "alice@example.com", active: true și un obiect imbricat details care conține age: 30 și un address cu city: "New York" și country: "USA". Afișează user.name.toUpperCase(), user.details.age.toFixed(0) și user.details.address.city.toLowerCase() și lasă comentate trei accesări la proprietăți care nu există (user.age, user.details.name, user.details.address.zip), notând erorile pe care le-ar produce.
Ieșire: program TypeScript și rezultatul capturat

Tipare avansate

Alte două tipare schimbă comportamentul inferenței: aserțiunile as const fac tipurile deduse cât mai restrânse posibil, iar analiza fluxului de control restrânge un tip uniune în interiorul blocurilor if și switch.

Aserțiuni const

Un let name = "Alice" obișnuit este lărgit la string. Cu as const tipul devine exact literalul "Alice", iar un obiect marcat as const devine în întregime doar-citire, cu tipuri literale pentru proprietăți.

Prompt
Arată diferența dintre inferența obișnuită și aserțiunile as const. Declară let name = "Alice" (lărgit la string) și const nameConst = "Alice" as const (restrâns la literalul "Alice"). Apoi creează un obiect user cu id: 1, name: "Alice" și un tablou roles cu "admin" și "user" marcat as const (un tuplu doar-citire), întregul obiect fiind și el marcat as const. Lasă user.name = "Bob" comentat, cu o notă că ar fi o eroare de proprietate doar-citire. Nu se afișează nimic.
Ieșire: program TypeScript și rezultatul capturat

Gărzi de tip și analiza fluxului de control

În interiorul if (typeof value === "string") tipul lui value este restrâns la string, iar în ramura else la number. În area, switch-ul pe proprietatea kind restrânge shape la Circle sau Square, așa că radius și size pot fi folosite în siguranță.

Prompt
Arată restrângerea tipurilor prin analiza fluxului de control, cu o gardă de tip typeof și o uniune discriminată. Scrie processValue(value: string | number) care afișează valoarea cu majuscule când typeof value === "string" și value.toFixed(2) în rest. Apoi declară interfețele Circle (kind: "circle", radius) și Square (kind: "square", size), o uniune Shape și o funcție area(shape: Shape) al cărei switch pe shape.kind returnează Math.PI * radius ** 2 pentru cercuri și size ** 2 pentru pătrate. Doar declară funcțiile; nu se afișează nimic.
Ieșire: program TypeScript și rezultatul capturat

Bune practici

Iată câteva bune practici pentru lucrul cu inferența tipurilor din TypeScript:

Exemplu

Lasă valorile simple să fie deduse, scrie întotdeauna tipurile parametrilor funcțiilor, adaugă tipuri returnate la funcțiile complexe, adnotează tablourile și obiectele goale (altfel devin any[] sau {}) și folosește o aserțiune de tip atunci când TypeScript nu poate cunoaște tipul exact. Căutarea în DOM este protejată printr-o verificare, ca fișierul să ruleze și în afara browserului.

Prompt
Arată cinci bune practici pentru lucrul cu inferența de tip, fiecare introdusă de un comentariu numerotat.
  1. Lasă un simplu let message = "Salut" să fie dedus. 2) Dă-i lui formatName(firstName: string, lastName: string) tipuri explicite pentru parametri. 3) Dă-i lui processData(input: string[]) un tip returnat explicit { count: number; items: string[] } și returnează numărul de elemente plus elementele curățate de spații. 4) Adnotează un tablou gol ca string[] și un obiect gol ca Record<string, unknown>. 5) Folosește o aserțiune de tip as HTMLCanvasElement pe document.getElementById("main-canvas"), protejată de o verificare typeof document !== "undefined", ca fișierul să ruleze și în afara browserului. Nu se afișează nimic.
Ieșire: program TypeScript și rezultatul capturat

Când să folosești tipuri explicite

Inferența tipurilor este puternică, dar în unele situații o adnotare explicită de tip este alegerea mai bună.

Recomandat pentru tipuri explicite:

  • Contracte de API public: parametrii și tipurile returnate ale funcțiilor din codul de bibliotecă
  • Tipuri complexe: când tipul dedus este prea larg sau greu de citit
  • Documentare: pentru ca acel cod să se explice singur
  • Siguranța tipurilor: când trebuie să impui constrângeri specifice
  • Colecții goale: tablouri sau obiecte goale care sunt completate mai târziu

Tipurile explicite pentru valorile returnate complexe și pentru obiectele de configurare ajută compilatorul și editorul să rămână rapide, pentru că nu mai trebuie recalculate din modul de utilizare.

Tipuri explicite pentru valori complexe

Trei cazuri în care merită scrisă o adnotare explicită: un tip returnat complex, un tablou gol care va fi completat mai târziu și un obiect de configurare.

script.ts
// Good: Explicit type for complex return values
function processData(input: string[]): { results: string[]; count: number } {
  return {
    results: input.map(processItem),
    count: input.length
  };
}

// Good: Explicit type for empty arrays
const items: Array<{ id: number; name: string }> = [];

// Good: Explicit type for configuration objects
const config: {
  apiUrl: string;
  retries: number;
  timeout: number;
} = {
  apiUrl: "https://api.example.com",
  retries: 3,
  timeout: 5000
};