tipuri pentru DOM în TypeScript

Tipurile pentru pagina web (document, elemente, evenimente) vin din biblioteca dom a TypeScript.

Sintaxă
const el = document.querySelector<HTMLInputElement>("#email");
buton.addEventListener("click", (e: MouseEvent) => { ... });
// tsconfig: "lib": ["es2024", "dom"]

Ce face tipuri pentru DOM

TypeScript are tipuri pentru tot ce oferă browserul: document, window, HTMLElement și subtipurile lui (HTMLInputElement, HTMLButtonElement…), evenimentele (MouseEvent, KeyboardEvent), fetch, localStorage. Ele sunt în biblioteca dom, inclusă implicit sau prin opțiunea lib din tsconfig.json. Funcțiile în sine sunt explicate în secțiunea JavaScript.

Metodele care caută elemente pot să nu găsească nimic: querySelector returnează Element | null, deci trebuie verificat. Tipul exact se poate da ca parametru generic (querySelector<HTMLInputElement>); când selectorul este un nume de tag ("input", "a"), tipul este dedus automat. addEventListener("click", ...) știe că evenimentul este MouseEvent, iar e.target are tipul general EventTarget | null.

În Node.js, document nu există; codul de mai jos se rulează în browser (după compilare). Unele clase din biblioteca dom există și în Node.js: EventTarget, Event, CustomEvent, URL, AbortController.

Exemple

Exemplul 1: Formular într-o pagină web

Pagina conține <input id="nota">, <button> și <p id="rezultat">. Utilizatorul scrie 9 și apasă butonul.

const intrare = document.querySelector<HTMLInputElement>("#nota");
const buton = document.querySelector("button");          // HTMLButtonElement | null
const rezultat = document.getElementById("rezultat");    // HTMLElement | null

if (intrare && buton && rezultat) {
    buton.addEventListener("click", (e) => {             // e: MouseEvent
        const nota = intrare.valueAsNumber;
        rezultat.textContent = nota >= 5 ? `Promovat cu ${nota}` : "Nepromovat";
        console.log(e.type, nota);
    });
}

Rezultat în consola browserului

click 9

Exemplul 2: Element care poate lipsi

const intrare = document.querySelector<HTMLInputElement>("#nota");
console.log(intrare.value);

Eroarea afișată de tsc

exemplu.mts(2,13): error TS18047: 'intrare' is possibly 'null'.

Exemplul 3: Clase din biblioteca dom care merg și în Node.js

const catalog = new EventTarget();

catalog.addEventListener("nota", (e) => {
    const { elev, nota } = (e as CustomEvent<{ elev: string; nota: number }>).detail;
    console.log(`${elev} a primit ${nota}`);
});

catalog.dispatchEvent(new CustomEvent("nota", { detail: { elev: "Ana", nota: 10 } }));
const url = new URL("https://dexblog.ro/invata/typescript/?pagina=2");
console.log(url.searchParams.get("pagina"), url.pathname);

Rezultat

Ana a primit 10
2 /invata/typescript/

Greșeli frecvente

  • Faci document.querySelector("#nota").value. Elementul poate lipsi și, chiar dacă există, are tipul Element, fără value; dă tipul generic și verifică null.
  • Folosești e.target.value într-un handler. target este EventTarget; folosește e.currentTarget sau variabila elementului.
  • Compilezi cod de server cu biblioteca dom inclusă. tsc nu te oprește să folosești window acolo; scoate dom din lib pentru proiectele Node.js.

Comenzi înrudite

  • tipuri pentru Node.js: Tipurile pentru modulele Node.js (fs, path, process…) vin din pachetul @types/node.
  • null și undefined: Cu strictNullChecks, null și undefined sunt tipuri separate: o variabilă le acceptă doar dacă tipul ei le include.
  • generics (<T>): Parametri de tip: o funcție sau clasă scrisă o dată, care funcționează cu orice tip și păstrează tipul exact.
  • as (type assertion): Îi spui compilatorului ce tip are o valoare, când știi mai mult decât el; nu face nicio conversie la rulare.
  • lib: Alege ce declarații de tipuri pentru mediul de rulare sunt incluse: versiunea JavaScript (es2022) și API-urile (dom, webworker).