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.
Gł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.
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.