Index Signature to mechanizm opisu typu obiektów, których klucze są z góry nieznane (lub mogą być dodawane dynamicznie), a wartości odpowiadają określonemu typowi. Umożliwia tworzenie obiektów-słowników (map/dictionary), gdzie lista właściwości jest nieznana na etapie kompilacji.
Historia pytania: w JavaScript obiekty często występują jako tablice asocjacyjne. Statyczna typizacja TypeScript wymaga wskazania typów kluczy i wartości. Index Signature rozwiązuje główny problem: opisanie obiektu, gdzie klucz — nie jest sztywno zapisanym właściwością, a stringiem (lub liczbą), a wartości — określonego typu.
Problem: podczas pracy z takimi obiektami można napotkać sytuację, w których znane właściwości obiektu kolidują z index signature, lub tracona jest informacja o konkretnej właściwości. Są pewne niuanse związane z dziedziczonymi typami i rozszerzeniem struktury słownika.
Rozwiązanie: Index Signature jest używany do opisu dynamicznych właściwości, zazwyczaj z kluczem typu string lub number, na przykład dla kolekcji, obiektów cache lub odpowiedzi API w formacie { [key: string]: T }. Aby zachować kontrolę nad typem, konieczne jest opisanie i stałych właściwości oraz index signature razem, lub, jeśli to konieczne — stworzenie typów union.
Przykład kodu:
interface StringNumberMap { [key: string]: number; } const map: StringNumberMap = { apples: 2, oranges: 5, bananas: 3 };
Kluczowe cechy:
Co się stanie, jeśli dodasz stałą właściwość z niekompatybilnym typem do interfejsu z index signature?
TypeScript zgłosi błąd, jeśli typ właściwości nie jest zgodny z typem określonym w index signature.
interface BadDict { [key: string]: number; error: string; // Błąd: string niezgodny z number }
Czy można zadeklarować kilka index signature z różnymi typami kluczy (number i string) jednocześnie?
Tak, jest to możliwe, ale istnieje pewien niuans: w TypeScript klucze typu number automatycznie konwertowane są na stringi. Index signature z number to tylko synonim dla kluczy string.
interface NumStrDict { [key: string]: number; [key: number]: number; }
Czy można używać typów union lub interfejsów dla wartości w index signature?
Tak, wartość może być typem union lub interfejsem. W ten sposób można opisać słowniki złożonych obiektów.
interface Dict { [key: string]: string | number; }
W interfejsie odpowiedzi API użyto [key: string]: any. Później zwracany obiekt zawierał dane różnych struktur, co doprowadziło do błędów w czasie wykonywania i utrudniło utrzymanie kodu.
Zalety:
Wady:
Opisano interfejs obiektu cache, gdzie wartości są ściśle określonego typu (np. UserData), z komentarzem, że klucz — userId (string):
interface UserCache { [userId: string]: UserData; }
Zalety:
Wady: