index signatures în TypeScript

Descrie obiecte cu chei necunoscute dinainte: [cheie: string]: Tip, sau Record<string, Tip>.

Sintaxă
interface Dictionar {
    [cheie: string]: number;
}
type Note = Record<string, number>;
type Partial = { [cheie: string]: number | undefined };

Ce face index signatures

Uneori nu știi dinainte numele proprietăților: un dicționar oraș → populație, note pe materii, setări citite dintr-un fișier. O index signature descrie astfel de obiecte: [cheie: string]: number înseamnă „orice cheie text, cu valoare număr”. Cheia poate fi string, number, symbol sau un template literal ([k: `data-${string}`]: string).

Record<string, number> este forma scurtă pentru același lucru și se citește mai ușor. Cu un union de literale în loc de string, Record<"lei" | "euro", number>, obiectul trebuie să aibă exact acele chei (vezi Record). Pentru dicționare care se modifică des, Map<string, number> este adesea o alegere mai bună.

Atenție: tipul spune că dictionar["orice"] este number, chiar dacă cheia nu există și valoarea este de fapt undefined. Opțiunea noUncheckedIndexedAccess adaugă undefined la tipul citit, ca să fii obligat să verifici. Proprietățile cu nume fix scrise lângă o index signature trebuie să aibă un tip compatibil cu ea.

Exemple

Exemplul 1: Dicționar cu chei text

interface Populatie {
    [oras: string]: number;
}

const populatie: Populatie = { "Iași": 271692, "Cluj-Napoca": 286598 };
populatie["Timișoara"] = 250849;

for (const [oras, locuitori] of Object.entries(populatie)) {
    console.log(oras.padEnd(12), locuitori.toLocaleString("ro-RO"));
}
console.log("Brașov" in populatie, populatie["Brașov"]);

Rezultat

Iași         271.692
Cluj-Napoca  286.598
Timișoara    250.849
false undefined

Exemplul 2: Record și numărarea cuvintelor

Record<string, number> este echivalent cu { [cheie: string]: number }. ?? 0 tratează cheile care încă nu există.

function numara(text: string): Record<string, number> {
    const rezultat: Record<string, number> = {};
    for (const cuvant of text.toLowerCase().split(/\s+/)) {
        rezultat[cuvant] = (rezultat[cuvant] ?? 0) + 1;
    }
    return rezultat;
}

console.log(numara("Ana are mere și Ana are pere"));

Rezultat

{ ana: 2, are: 2, mere: 1, 'și': 1, pere: 1 }

Exemplul 3: Proprietate fixă incompatibilă cu index signature

Toate valorile trebuie să fie number, inclusiv proprietatea cu nume fix moneda.

interface Preturi {
    moneda: string;
    [produs: string]: number;
}
const p: Preturi = { moneda: "lei", paine: 5 };
console.log(p);

Eroarea afișată de tsc

exemplu.mts(2,5): error TS2411: Property 'moneda' of type 'string' is not assignable to 'string' index type 'number'.
exemplu.mts(5,7): error TS2322: Type '{ moneda: string; paine: number; }' is not assignable to type 'Preturi'.
  Property 'moneda' is incompatible with index signature.
    Type 'string' is not assignable to type 'number'.

Greșeli frecvente

  • Folosești { [k: string]: any } pentru orice obiect. Pierzi toată verificarea; descrie proprietățile cunoscute.
  • Crezi că note["fizică"] este sigur pentru că are tipul number. Cheia poate lipsi; verifică cu in sau ??.

Comenzi înrudite

  • Record: Record<K, T> descrie un obiect ale cărui chei sunt K și ale cărui valori au toate tipul T.
  • interface: Descrie forma unui obiect: ce proprietăți are, de ce tip sunt și ce metode are.
  • mapped types: Construiesc un tip nou trecând prin fiecare cheie a altui tip: { [K in keyof T]: … }.
  • keyof: keyof T dă union-ul numelor de proprietăți ale lui T, de exemplu „nume” | „oras”.