0Pricing
TypeScript Academy · Lekcja

Głębokość i ograniczenia rekurencji

Poznaj ograniczenia głębokości rekurencji w TypeScript

Głębokość i ograniczenia rekurencji to bezpłatna lekcja TypeScript Academy na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej TypeScript Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs TypeScript Academy zawiera 4 lekcji w sumie.

TypeScript ma limity rekurencji

Typy rekurencyjne są potężne, ale narzędzie sprawdzające typy ogranicza głębokość ich instancjowania. Po przekroczeniu limitu pojawia się błąd głębokości.

// Excessively deep recursive types can produce:
// "Type instantiation is excessively deep and possibly infinite."

Skąd biorą się błędy głębokości

Typy warunkowe, które rekurują bez wyraźnego, zmniejszającego się przypadku bazowego, mogą wymknąć się spod kontroli, zwłaszcza w połączeniu z dużymi uniami lub budowaniem krotek.

type Repeat<N extends number, Acc extends unknown[] = []> =
  Acc["length"] extends N ? Acc : Repeat<N, [...Acc, unknown]>;
// Large N can exceed the instantiation depth.
type Five = Repeat<5>;

Bezpieczna rekurencja z ograniczeniem

Rekurencja powinna być płytka i mieć ograniczenie. Zbudowanie niewielkiej krotki jest bezpieczne, a przypadek bazowy Acc["length"] extends N szybko ją zatrzymuje.

type Tuple<N extends number, Acc extends unknown[] = []> =
  Acc["length"] extends N ? Acc : Tuple<N, [...Acc, unknown]>;
type Three = Tuple<3>; // [unknown, unknown, unknown]
const t: Three = [1, 2, 3];
console.log(t.length);

Wzorce rekurencji ogonowej

TypeScript optymalizuje niektóre warunkowe typy rekurencyjne ogonowo, przekazując akumulator, co pozwala na głębszą rekurencję niż naiwne zagnieżdżanie.

type Reverse<T extends unknown[], Acc extends unknown[] = []> =
  T extends [infer Head, ...infer Tail]
    ? Reverse<Tail, [Head, ...Acc]>
    : Acc;
type R = Reverse<[1, 2, 3]>; // [3, 2, 1]
const r: R = [3, 2, 1];
console.log(r);

Dlaczego akumulatory pomagają

Przekazywanie wyników w akumulatorze utrzymuje rekurencję w pozycji ogonowej, dzięki czemu TypeScript może rozwijać ją wydajniej niż głęboko zagnieżdżone typy warunkowe.

type Join<T extends string[], Acc extends string = ""> =
  T extends [infer H extends string, ...infer R extends string[]]
    ? Join<R, Acc extends "" ? H : Acc>
    : Acc;
type First = Join<["a", "b", "c"]>; // "a"
const f: First = "a";
console.log(f);

Unikanie rekurencji nieogonowej

Umieszczenie wywołania rekurencyjnego wewnątrz innej operacji na typie przerywa pozycję ogonową i może szybciej doprowadzić do przekroczenia limitów. Zamiast tego należy przechowywać stan w akumulatorze.

// Non-tail (can be costly): builds nesting around the recursive call
// type Bad<T> = T extends [infer H, ...infer R] ? [H, ...Bad<R>] : [];
// Tail-friendly alternative uses an accumulator parameter.
console.log("prefer accumulators");

Jawne ograniczanie głębokości

Należy dodać licznik głębokości, aby typ kończył działanie po ustalonej liczbie poziomów, zamieniając kompletność na gwarantowane zakończenie.

type Flatten<T, Depth extends unknown[] = []> =
  Depth["length"] extends 5
    ? T
    : T extends (infer U)[]
      ? Flatten<U, [...Depth, unknown]>
      : T;
type X = Flatten<number[][]>; // number
const x: X = 7;
console.log(x);

Praktyczna głębokość zwykle wystarcza

Codzienne typy rekurencyjne, listy wiązane, drzewa i JSON zwykle nigdy nie osiągają limitu, ponieważ instancjonowane dane są płytkie. Limity dotyczą głównie intensywnych obliczeń na poziomie typów.

type Json = string | number | boolean | null | Json[] | { [k: string]: Json };
const v: Json = { a: { b: { c: 1 } } }; // fine, shallow
console.log(v);

Rozpoznawanie komunikatu o błędzie

Gdy pojawi się komunikat "excessively deep and possibly infinite", należy poszukać brakującego przypadku bazowego, akumulatora w pozycji nieogonowej albo zbyt dużych danych wejściowych.

// Fixes: add/tighten the base case, switch to tail recursion,
// reduce input size, or cap with a depth counter.
console.log("check base case and tail position");

Rekurencja w czasie działania to osobna kwestia

Te limity dotyczą rekurencji na poziomie typów. Zwykłe funkcje rekurencyjne operujące na typach rekurencyjnych działają w czasie wykonania i są ograniczone tylko rozmiarem stosu wywołań.

type TreeNode<T> = { value: T; children: TreeNode<T>[] };
function count<T>(n: TreeNode<T>): number {
  return 1 + n.children.reduce((a, c) => a + count(c), 0);
}
console.log(count({ value: 1, children: [{ value: 2, children: [] }] }));

Projektowanie z uwzględnieniem limitów

Należy preferować płytkie dane, wzorce rekurencji ogonowej z akumulatorami oraz jawne ograniczenia głębokości. Dzięki tym zasadom w prawdziwym kodzie rzadko napotkają Państwo ten limit.

type Length<T extends unknown[]> = T["length"];
type N = Length<[1, 2, 3]>; // 3
const n: N = 3;
console.log(n);

Szybkie sprawdzenie: limity rekurencji

Proszę sprawdzić swoją wiedzę na temat głębokości rekurencji i limitów.

Podsumowanie: głębokość i limity rekurencji

Dowiedzieli się Państwo, że TypeScript ogranicza głębokość rekurencji na poziomie typów, że pomagają wzorce rekurencji ogonowej z akumulatorami i jawne ograniczenia głębokości oraz że codzienne dane rekurencyjne rzadko osiągają ten limit.

type Reverse<T extends unknown[], Acc extends unknown[] = []> =
  T extends [infer H, ...infer R] ? Reverse<R, [H, ...Acc]> : Acc;
const r: Reverse<[1, 2]> = [2, 1];
console.log(r);

Często zadawane pytania

Czy lekcja „Głębokość i ograniczenia rekurencji” jest bezpłatna?

Tak — pełny tekst „Głębokość i ograniczenia rekurencji” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu TypeScript Academy, przejdź na CoddyKit PRO. Kurs TypeScript Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Głębokość i ograniczenia rekurencji”?

Poznaj ograniczenia głębokości rekurencji w TypeScript Ćwiczysz TypeScript Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć TypeScript Academy?

Nie wymagamy żadnego doświadczenia. TypeScript Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.

Ile czasu zajmuje lekcja „Głębokość i ograniczenia rekurencji”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji TypeScript Academy?

Tak. Każda lekcja TypeScript Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Rekurencyjne definicje typów
  2. Typowanie struktur drzewiastych
  3. Typy wartości JSON
  4. Głębokość i ograniczenia rekurencji
← Powrót do TypeScript Academy