Krotka (tuple) w TypeScript to tablica o stałej liczbie elementów, w której każda pozycja ma własny typ. [string, number] oznacza dokładnie dwa elementy: najpierw string, potem liczbę. Typy zapisujesz w nawiasach kwadratowych w tej kolejności, w jakiej pojawiają się wartości.
W czasie wykonania krotka to zwykła tablica JavaScriptu. Wszystko, co dodaje krotka (stała długość i typ na każdej pozycji), sprawdza kompilator, a potem to usuwa.
Składnia krotek
| Typ krotki | Przyjmuje | Typ length |
|---|---|---|
[string, number] | dokładnie string, a potem liczbę | 2 |
[x: number, y: number] | to samo, z etykietami dla czytelności | 2 |
[number, number, number?] | 2 albo 3 liczby | 2 | 3 |
[string, ...number[]] | string, a potem dowolną liczbę liczb | number |
[...string[], number] | dowolną liczbę stringów, a potem liczbę | number |
readonly [number, number] | parę, której nie da się zmienić | 2 |
[] | tylko pustą tablicę | 0 |
Każda forma jest omówiona niżej. Warto zwrócić uwagę na typ length: dla krotki o stałej długości to typ literału, więc kompilator wie, że pair.length to dokładnie 2.
Co sprawdza kompilator
Typ krotki ustala liczbę elementów, ich kolejność i typ na każdej pozycji. Pomyłka w którejkolwiek z tych rzeczy to błąd kompilacji:
index.ts(2,7): error TS2322: Type '[string]' is not assignable to type '[string, number]'.
Source has 1 element(s) but target requires 2.
index.ts(3,36): error TS2322: Type 'number' is not assignable to type 'string'.
index.ts(3,40): error TS2322: Type 'string' is not assignable to type 'number'.
index.ts(5,16): error TS2493: Tuple type '[string, number]' of length '2' has no element at index '2'.
Zwykła tablica nigdy nie wyłapałaby ostatniego błędu: dla string[] wyrażenie arr[2] to po prostu string, który w czasie wykonania okazuje się undefined.
Nazwane elementy krotki
Etykiety opisują, co oznacza każda pozycja. Nie zmieniają niczego w typie ani w sposobie indeksowania, ale edytory pokazują je w podpowiedziach i sygnaturach, dzięki czemu [number, number] staje się dużo mniej tajemnicze.
Od TypeScript 5.2 możesz oznaczyć etykietami tylko część pozycji, jak w [first: string, number]. Etykiety są tylko dla czytelnika: [x: number, y: number] i [number, number] to ten sam typ i można je przypisywać jeden do drugiego.
Elementy opcjonalne
? po typie elementu sprawia, że ta pozycja jest opcjonalna. Elementy opcjonalne muszą stać po wymaganych, a każdy z nich poszerza typ length.
Odczyt elementu opcjonalnego daje T | undefined, więc przed działaniami arytmetycznymi potrzebna jest wartość domyślna we wzorcu destrukturyzacji (a = 1) albo sprawdzenie.
Elementy rest
Element rest, ...T[], oznacza dowolną liczbę elementów typu T. Może stać na końcu, na początku albo w środku, najwyżej jeden na krotkę.
length krotki z elementem rest ma typ number, bo rozmiar nie jest już stały. Stałe pozostaje to, gdzie leżą otypowane pozycje.
Krotki readonly i as const
readonly [T, U] usuwa push, pop, splice i przypisanie przez indeks, czyli dokładnie to, czego nie powinna mieć wartość o stałej długości. as const po literale tablicy wnioskuje krotkę readonly z typami literałów.
(typeof SIZES)[number] zamienia krotkę w unię typów jej elementów. Ten wzorzec omawia strona o indexed access types. Krotki readonly nie da się przekazać do parametru o typie modyfikowalnej krotki, więc funkcje, które tylko czytają, powinny przyjmować readonly [number, number].
Sprawdzenie readonly działa tylko w czasie kompilacji. W czasie wykonania tablica nie jest zamrożona (przypisanie powyżej naprawdę się wykonało, co widać na wyjściu), więc jeśli potrzebujesz gwarancji w czasie wykonania, użyj Object.freeze.
Zwracanie krotki z funkcji
Zwracanie kilku wartości jako krotki to sposób działania useState w React (const [value, setValue] = useState(0)). Haczyk: literał tablicy w return jest wnioskowany jako tablica, a nie krotka.
index.ts(9,13): error TS2365: Operator '+' cannot be applied to types 'number | (() => number)' and 'number'.
index.ts(10,1): error TS2349: This expression is not callable.
Not all constituents of type 'number | (() => number)' are callable.
Type 'number' has no call signatures.
Funkcja zwraca (number | (() => number))[], więc obie zmienne z destrukturyzacji dostają typ unii. Są dwie poprawki: podać typ zwracany albo dodać as const.
Krotka jako wartość zwracana pozwala wywołującym nazwać części, jak chcą. Gdy wartości jest więcej niż dwie albo trzy albo kolejność nie jest oczywista, zwróć obiekt: { count, increment } sam się dokumentuje.
Krotki jako parametry funkcji
Parametr rest o typie krotki opisuje całą listę argumentów, także argumenty opcjonalne. Tak wbudowany typ narzędziowy Parameters<T> przedstawia parametry funkcji.
Rozwinięcie krotki w wywołaniu sprawdza typ każdego argumentu według pozycji, czego nie potrafi rozwinięcie (string | number)[].
Krotka a tablica
Tablica (string | number)[] | Krotka [string, number] | |
|---|---|---|
| Długość | dowolna | stała (albo ograniczona przez elementy opcjonalne i rest) |
Typ x[0] | string | number | string |
Typ x[5] | string | number | błąd kompilacji TS2493 |
Typ length | number | 2 |
| Kolejność typów | nieśledzona | śledzona |
| Wartość w czasie wykonania | tablica JavaScriptu | ta sama tablica JavaScriptu |
| Typowe użycie | listy podobnych elementów | małe stałe grupy: pary, współrzędne, [key, value], kilka wartości zwracanych |
Krotki pojawiają się też we wbudowanych typach. Object.entries(obj) zwraca [string, T][], a Map tworzy się z krotek [key, value]:
Jedna pułapka: modyfikowalna krotka nadal ma wszystkie metody tablic, więc pair.push(3) kompiluje się na [string, number] i po cichu tworzy tablicę z trzema elementami, choć typ mówi o dwóch. Deklarowanie krotek jako readonly zamyka tę lukę. A ponieważ typy są usuwane, dane spoza programu (JSON, API) nie są w czasie wykonania sprawdzane względem typu krotki: zanim im zaufasz, sprawdź ich długość i typy elementów.
Variadic tuple types
Typy krotek mogą rozwijać inne typy krotek, [...T, ...U]. W połączeniu z generykami pozwala to otypować funkcje, które łączą albo doklejają elementy na początku, zachowując każdą pozycję:
Typy bibliotek też korzystają z wnioskowania krotek: Promise.all([fetchUser(), fetchPosts()]) rozwiązuje się do krotki z jednym typem na każdy wejściowy promise.
Najczęściej zadawane pytania
Czym jest krotka (tuple) w TypeScript?
Krotka to typ tablicy o stałej długości, w którym każda pozycja ma własny typ: [string, number] to dokładnie dwa elementy, najpierw string, potem liczba. W czasie wykonania to zwykła tablica JavaScriptu, a długość i typy na poszczególnych pozycjach są sprawdzane tylko w czasie kompilacji.
Czym różni się krotka od tablicy w TypeScript?
Typ tablicy, taki jak (string | number)[], ma dowolną długość, a każdy element ma ten sam typ (unię), więc arr[0] to string | number. Krotka, taka jak [string, number], ma znaną długość, t[0] to string, t[1] to number, a t[2] to błąd kompilacji.
Jak zwrócić krotkę z funkcji w TypeScript?
Podaj typ zwracany, function f(): [number, string], albo zakończ zwracane wyrażenie przez as const, co daje krotkę readonly. Bez żadnego z nich return [count, setCount] jest wnioskowane jako tablica unii, na przykład (number | (() => void))[], a destrukturyzacja daje typy unii.
Czym są nazwane elementy krotki?
To etykiety na pozycjach, [name: string, age: number]. Nie zmieniają typu ani sposobu dostępu (nadal t[0]), ale edytory pokazują je w podpowiedziach po najechaniu i w podpowiedziach parametrów funkcji, których parametry mają typ krotki. Elementy opcjonalne i rest działają z etykietami: [x: number, y?: number], [head: string, ...rest: number[]].
Czy można zrobić push do krotki w TypeScript?
Do modyfikowalnej krotki tak: push się kompiluje, bo krotki dziedziczą metody tablic, mimo że psuje to stałą długość. Zadeklaruj krotkę jako readonly (albo utwórz ją przez as const), a push, pop i przypisanie przez indeks staną się błędami kompilacji.