Zagadnienie 10 z 12
Rekurencja
Na poziomie typów nie ma pętli, więc rekurencja to jedyny sposób przetworzenia czegoś o nieznanej długości. Kształt jest zawsze ten sam: oddziel kawałek, obsłuż go, wywołaj się na reszcie i zatrzymaj, gdy nic nie zostanie.
- Wyrażenie
Unwrap<Promise<Promise<string>>> - Rozwija się do
string - Wyrażenie
Unwrap<Promise<string>> - Rozwija się do
string - Wyrażenie
Unwrap<string> - Rozwija się do
string
Każde wywołanie rekurencyjne usuwa jedną warstwę Promise. Końcowy string to przypadek bazowy: zwróć T i zakończ.
Definicja typu
type Unwrap<T> = T extends Promise<infer U> ? Unwrap<U> : TGłowa i ogon
type Reverse<T extends readonly unknown[]> = T extends readonly [infer H, ...infer R] ? [...Reverse<R>, H] : [] // The order you REBUILD in is the whole difference: // [H, ...Reverse<R>] copies // [...Reverse<R>, H] reverses
Gałąź fałszywa to wyjście i musi zwracać element neutralny tego, co budujesz: [] dla krotek, '' dla łańcuchów, {} dla obiektów. Zwróć tam ładunek zamiast tego, a dostaniesz błąd o jeden, widoczny dopiero na końcu.
Akumulatory budują wprzód
type Unique<T extends readonly unknown[], Acc extends unknown[] = []> = T extends readonly [infer H, ...infer R] ? Includes<Acc, H> extends true ? Unique<R, Acc> : Unique<R, [...Acc, H]> : Acc
Parametr z wartością domyślną niesie stan, którego wywołujący nigdy nie podaje. Poza poprawnością — to dzięki temu Unique zachowuje *pierwsze* wystąpienie, a nie ostatnie — kształt rekurencji ogonowej zużywa też mniej stosu kompilatora niż składanie wyniku przy wychodzeniu.
Istnieje limit głębokości
// Around 1000 levels, then: // "Type instantiation is excessively deep and possibly infinite."
TypeScript zatrzymuje się mniej więcej po tysiącu instancjacji. Rekurencja ogonowa znacznie podnosi ten pułap, ale limit jest realny — i dlatego nikt nie wydaje kalkulatora na typach obsługującego duże liczby.
Najczęstsza błędna odpowiedź
// Intent: flatten completely, to any depth. type Flatten<T extends readonly unknown[]> = T extends readonly [infer H, ...infer R] ? H extends readonly unknown[] ? [...H, ...Flatten<R>] // <- only unwraps ONE layer : [H, ...Flatten<R>] : [] Flatten<[[[1]]]> // [[1]] — stopped one level early
Rozwinięcie H otwiera jedną warstwę; rekurencja w nią otwiera wszystkie. Rekurencja w jednym kierunku przechodzi listę, w dwóch — drzewo, a „zeszło tylko o poziom” to prawie zawsze brak drugiego wywołania.
Ćwiczenie
Rozpakuj obietnicę, niezależnie od tego, ile ma warstw.
Spróbuj
2Unwrap<Promise<string>>—→ stringUnwrap<Promise<Promise<number>>>—→ number
Do zapamiętania
Podziel, obsłuż, wywołaj się, zatrzymaj. Przypadek bazowy zwraca element neutralny, a kierunek składania odróżnia kopię od odwrócenia, filtra czy mapowania.
Ćwiczone w
- #215Głęboko tylko do odczytuPar 4
- #222OdwróćPar 4
- #227ZawieraPar 3
- #229SpłaszczPar 4
- #237UnikalnePar 4
- #240FiltrujPar 3
- #245ŚcieżkiPar 5
- #251Opcjonalne aż do dnaPar 4
- #259Pobierz po ścieżcePar 5
- #260Jak długi jestPar 4
- #265Które większePar 4
- #266Zepnij obiektPar 5
- #271Usuń znakPar 3
- #272Krotka w zagnieżdżony obiektPar 4

