Przejdź do treści
webtype.orgDwanaście zagadnień · 10/12

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.

Ćwiczone w