Semnături de index în TypeScript
O semnătură de index îți permite să descrii un obiect ale cărui nume de proprietăți nu le cunoști dinainte, spunând totuși ce tip trebuie să aibă fiecare valoare.
Înțelegerea semnăturilor de index în TypeScript
Semnăturile de index îți oferă o modalitate de a tipiza obiecte cu nume de proprietăți dinamice fără a pierde siguranța tipurilor.
Ele îi spun lui TypeScript ce tip de valoare se obține atunci când citești o proprietate cu notația cu paranteze drepte (obj[key]), chiar și atunci când numele exacte ale proprietăților nu sunt cunoscute dinainte.
Idei principale:
- Acces dinamic la proprietăți: lucrezi cu obiecte care au orice nume de proprietăți.
- Siguranța tipurilor: fiecare proprietate dinamică trebuie să conțină același fel de valoare.
- Structuri de date flexibile: modelezi dicționare, hărți și alte tabele de căutare.
- Erori timpurii: greșelile legate de tipurile valorilor sunt prinse la compilare.
Semnături de index de bază
Semnături de index de tip string. Folosește o semnătură de index atunci când nu cunoști dinainte numele proprietăților, dar cunoști forma valorilor.
Sintaxa folosește paranteze drepte: [key: type] descrie tipul numelor de proprietăți (cheile), iar după două puncte vine tipul valorilor pe care le pot conține acele proprietăți.
Semnături de index numerice. TypeScript acceptă atât chei numerice, cât și chei de tip string.
Notă: În JavaScript, toate cheile obiectelor sunt stocate ca string-uri, chiar și cele numerice. TypeScript le deosebește totuși pentru a ajuta la depistarea erorilor de logică atunci când confunzi tablourile cu obiectele.
Semnătură de index de tip string
StringDictionary acceptă orice cheie de tip string, dar fiecare valoare trebuie să fie un string. Citirea, adăugarea și actualizarea cheilor funcționează, în timp ce atribuirea unui număr este o eroare.
StringDictionary ale cărei chei sunt string-uri și ale cărei valori sunt string-uri.
Creează un obiect names de acest tip cu firstName: "Alice", lastName: "Smith" și cheia "100" asociată valorii "O sută", afișează cele trei valori folosind accesul cu paranteze drepte, apoi adaugă o cheie age cu string-ul "30". Lasă o linie comentată care arată că atribuirea numărului 30 ar fi o eroare de tip.
Ieșire: program TypeScript și rezultatul capturat
// Această interfață reprezintă un obiect cu chei de tip string și valori de tip string
interface StringDictionary {
[key: string]: string;
}
// Crearea unui obiect conform
const names: StringDictionary = {
firstName: "Alice",
lastName: "Smith",
"100": "O sută"
};
// Accesarea proprietăților
console.log(names["firstName"]); // "Alice"
console.log(names["lastName"]); // "Smith"
console.log(names["100"]); // "O sută"
// Adăugarea dinamică de proprietăți noi
names["age"] = "30";
// Aceasta ar provoca o eroare
// names["age"] = 30; // Eroare: tipul 'number' nu poate fi atribuit tipului 'string'
Semnătură de index numerică
NumberDictionary folosește chei numerice. Deoarece tipul valorii este any, fiecare intrare poate conține un alt fel de valoare.
NumberDictionary ale cărei chei sunt numere și ale cărei valori sunt any.
Creează un obiect scores cu 0: "Zero", 1: 100 și 2: true, afișează cele trei intrări după indicele numeric, apoi adaugă indicele 3 care conține obiectul { passed: true }.
Ieșire: program TypeScript și rezultatul capturat
// Obiect cu indici numerici
interface NumberDictionary {
[index: number]: any;
}
const scores: NumberDictionary = {
0: "Zero",
1: 100,
2: true
};
console.log(scores[0]); // "Zero"
console.log(scores[1]); // 100
console.log(scores[2]); // true
// Adăugarea unui obiect complex
scores[3] = { passed: true };
Tipare avansate de semnături de index
Tipuri de proprietăți mixte. Poți combina o semnătură de index cu proprietăți numite explicit. Proprietățile numite sunt obligatorii; tot restul trebuie să corespundă semnăturii de index.
Important: Când combini proprietăți explicite cu o semnătură de index, tipul fiecărei proprietăți explicite trebuie să poată fi atribuit tipului de valoare al semnăturii de index.
Semnături de index readonly. Adaugă readonly la o semnătură de index pentru a împiedica modificarea valorilor după crearea obiectului.
Pentru restrângerea mulțimilor de chei și transformarea formelor, vezi lecția despre tipuri mapate.
Tipuri de proprietăți mixte
UserInfo cere name și age și permite orice altă proprietate atâta timp cât valoarea ei este un string sau un number.
UserInfo: name: string și age: number obligatorii, plus o semnătură [key: string]: string | number pentru orice altă proprietate.
Creează un obiect user valid cu name: "Alice", age: 30, address: "123 Main St" și zipCode: 12345. Adaugă un invalidUser comentat care arată că age: "thirty" și isAdmin: true ar fi fiecare erori de tip. Fișierul nu afișează nimic.
Ieșire: program TypeScript și rezultatul capturat
interface UserInfo {
name: string; // Proprietate obligatorie cu nume specific
age: number; // Proprietate obligatorie cu nume specific
[key: string]: string | number; // Toate celelalte proprietăți trebuie să fie string sau number
}
const user: UserInfo = {
name: "Alice", // Obligatorie
age: 30, // Obligatorie
address: "123 Main St", // Opțională
zipCode: 12345 // Opțională
};
// Aceasta ar provoca o eroare
// const invalidUser: UserInfo = {
// name: "Bob",
// age: "thirty", // Eroare: tipul 'string' nu poate fi atribuit tipului 'number'
// isAdmin: true // Eroare: tipul 'boolean' nu poate fi atribuit tipului 'string | number'
// };
Semnătură de index readonly
Citirea lui names[0] este permisă, dar scrierea la orice index al unui ReadOnlyStringArray este o eroare de compilare.
ReadOnlyStringArray cu o semnătură de index readonly [index: number]: string.
Atribuie tabloul ["Alice", "Bob", "Charlie"] unei variabile names de acest tip, afișează names[0] și lasă o atribuire comentată către names[0] care explică faptul că scrierea printr-o semnătură de index readonly este o eroare de tip.
Ieșire: program TypeScript și rezultatul capturat
interface ReadOnlyStringArray {
readonly [index: number]: string;
}
const names: ReadOnlyStringArray = ["Alice", "Bob", "Charlie"];
console.log(names[0]); // "Alice"
// Aceasta ar provoca o eroare
// names[0] = "Andrew"; // Eroare: semnătura de index din tipul 'ReadOnlyStringArray' permite doar citirea
Exemple din lumea reală
Gestionarea răspunsurilor de API. Semnăturile de index sunt utile pentru răspunsurile de API în care numele resurselor și câmpurile suplimentare de metadate nu sunt fixe.
Gestionarea răspunsurilor de API
ApiResponse<T> permite ca data să conțină orice nume de resursă asociat unui tablou de T și permite ca meta să poarte câmpuri suplimentare pe lângă page și total.
ApiResponse<T> al cărei data asociază orice nume de resursă ([resourceType: string]) cu T[], și al cărei meta are page: number, total: number plus o semnătură [key: string]: any pentru metadate suplimentare.
Declară o interfață User (id, name, email), construiește un apiResponse: ApiResponse<User> cu doi utilizatori (Alice și Bob) și meta page: 1, total: 2, timestamp: "2023-01-01T00:00:00Z", apoi afișează numele primului utilizator.
Ieșire: program TypeScript și rezultatul capturat
// Tip pentru răspunsuri de API cu chei dinamice
interface ApiResponse<T> {
data: {
[resourceType: string]: T[]; // de ex., { "users": User[], "posts": Post[] }
};
meta: {
page: number;
total: number;
[key: string]: any; // Permite metadate suplimentare
};
}
// Exemplu de utilizare cu un API de utilizatori
interface User {
id: number;
name: string;
email: string;
}
// Răspuns de API simulat
const apiResponse: ApiResponse<User> = {
data: {
users: [
{ id: 1, name: "Alice", email: "[email protected]" },
{ id: 2, name: "Bob", email: "[email protected]" }
]
},
meta: {
page: 1,
total: 2,
timestamp: "2023-01-01T00:00:00Z"
}
};
// Accesarea datelor
const users = apiResponse.data.users;
console.log(users[0].name); // "Alice"
Bune practici
Ce să faci:
- Folosește semnături de index pentru colecții cu chei dinamice.
- Combină-le cu proprietăți explicite pentru câmpurile pe care le cunoști.
- Păstrează tipurile valorilor specifice — evită
any. - Folosește
readonlyatunci când valorile nu trebuie să se schimbe.
Ce să eviți:
- Să recurgi la o semnătură de index atunci când cheile sunt cunoscute — preferă o interfață fixă.
- Să uiți că fiecare proprietate, inclusiv cele explicite, trebuie să se potrivească cu tipul de valoare al semnăturii de index.
- Să reinventezi tipurile mapate — folosește-le pentru transformări de formă.
Capcane frecvente
O greșeală frecventă este declararea unei proprietăți explicite al cărei tip nu se potrivește cu semnătura de index. Lărgește tipul de valoare al semnăturii de index pentru a rezolva problema.
Conflict între tipul proprietății și tipul de index
ConflictingTypes eșuează pentru că name: string nu se potrivește cu semnătura de index number; FixedTypes lărgește tipul valorii la number | string, astfel încât ambele proprietăți sunt permise.
interface ConflictingTypes {
[key: string]: number;
name: string; // Error: not assignable to string index type 'number'
}
interface FixedTypes {
[key: string]: number | string;
name: string; // OK
age: number; // OK
}
Semnături de index vs Record
Folosește o semnătură de index atunci când cheile sunt flexibile sau dinamice și când ai nevoie să adaugi și alte proprietăți.
Folosește Record<K, T> pentru asocieri simple și concise de la un tip de cheie la un tip de valoare.
Semnătură de index și Record una lângă alta
StringMap și StringRecord descriu aceeași formă — orice cheie de tip string asociată unei valori de tip string — scrisă în două moduri diferite.
// Index signature
interface StringMap {
[key: string]: string;
}
// Record
type StringRecord = Record<string, string>;
Concluzie
Folosește semnături de index atunci când cheile sunt dinamice, dar forma valorilor este consecventă.
Combină-le cu proprietăți explicite ori de câte ori este posibil și vezi lecțiile despre tipuri mapate și tipuri utilitare pentru transformări mai avansate.