0Pricing
TypeScript Academy · Lesson

Type-Level Recursion

Loop over types using recursive conditional types.

Type-Level Recursion is a free TypeScript Academy lesson on CoddyKit — lesson 3 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the TypeScript Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

Recursion in Types

A conditional type may refer to itself. That gives the type language loops. Most type-level recursion walks a tuple one element at a time, peeling off the head and recursing on the tail.

type Length<T extends unknown[]> =
  T extends [unknown, ...infer Rest]
    ? Length<Rest>
    : 0;
// (this counts down to a base case)

The Base Case

Every recursion needs a stopping condition. For tuples it is usually the empty tuple. When the pattern [head, ...rest] no longer matches, you have hit the end and return a fixed result.

type IsEmpty<T extends unknown[]> =
  T extends [] ? true : false;

type A = IsEmpty<[]>;     // true
type B = IsEmpty<[1, 2]>; // false

Head and Tail

The core move is splitting a tuple into its first element and the remaining tuple, using infer with the spread pattern.

type Head<T extends unknown[]> = T extends [infer H, ...unknown[]] ? H : never;
type Tail<T extends unknown[]> = T extends [unknown, ...infer R] ? R : [];

type A = Head<[1, 2, 3]>; // 1
type B = Tail<[1, 2, 3]>; // [2, 3]

Processing Element by Element

Combine head, tail, and recursion to transform a whole tuple. ToStrings rebuilds the tuple, replacing each element type with string.

type ToStrings<T extends unknown[]> =
  T extends [infer H, ...infer R]
    ? [string, ...ToStrings<R>]
    : [];

type A = ToStrings<[1, true, 3]>; // [string, string, string]

The Accumulator Pattern

Often you build up a result in an extra parameter called an accumulator. It starts empty and grows on each step. This avoids re-walking the structure and is the standard technique for type-level loops.

type Reverse<T extends unknown[], Acc extends unknown[] = []> =
  T extends [infer H, ...infer R]
    ? Reverse<R, [H, ...Acc]>
    : Acc;

type A = Reverse<[1, 2, 3]>; // [3, 2, 1]

Walking the Accumulator

Trace Reverse<[1,2,3]>:

  • Step 1: H=1, Acc becomes [1]
  • Step 2: H=2, Acc becomes [2,1]
  • Step 3: H=3, Acc becomes [3,2,1]
  • Tuple empty, return Acc = [3,2,1]

The accumulator carries the answer down each recursive call.

type R = Reverse<["a", "b"]>; // ["b", "a"]

Recursion Over Unions

You can also recurse to join a union of strings. Here we concatenate tuple elements into one string literal type, separated by a delimiter.

Notation: real TypeScript writes template literal types with backtick-delimited strings containing dollar-brace holes. In these snippets we show that pattern as Tpl<...>, listing each part in order; e.g. a backtick template matching the literal prefix then Rest appears as Tpl<'prefix', infer Rest>.

type Join<T extends string[], Sep extends string = ','> =
  T extends [infer H extends string, ...infer R extends string[]]
    ? R extends []
      ? H
      : Tpl<H, Sep, Join<R, Sep>>
    : '';
// Tpl<H, Sep, ...> builds a template literal type joining the parts

type A = Join<['a', 'b', 'c']>; // 'a,b,c'

Counting With Recursion

Recursion plus an accumulator can count. Build a tuple of unknown the same length as the input, then later read its length. You will use this heavily in the arithmetic course.

type Count<T extends unknown[], Acc extends unknown[] = []> =
  T extends [unknown, ...infer R]
    ? Count<R, [unknown, ...Acc]>
    : Acc["length"];

type A = Count<["x", "y", "z"]>; // 3

Filtering With Recursion

Walk a tuple and keep only elements matching a condition, dropping others. Skip an element by not adding it to the accumulator.

type KeepStrings<T extends unknown[], Acc extends unknown[] = []> =
  T extends [infer H, ...infer R]
    ? H extends string
      ? KeepStrings<R, [...Acc, H]>
      : KeepStrings<R, Acc>
    : Acc;

type A = KeepStrings<[1, "a", 2, "b"]>; // ["a", "b"]

Recursion Depth Limits

The compiler caps recursion depth (historically around 50, with tail-recursion optimizations allowing more in some patterns). For very large tuples you may hit Type instantiation is excessively deep. Use the accumulator (tail) style to push the limit higher.

type Repeat<T, N extends number, Acc extends T[] = []> =
  Acc["length"] extends N ? Acc : Repeat<T, N, [...Acc, T]>;

type A = Repeat<0, 3>; // [0, 0, 0]

Putting It Together

Head/tail splitting, a base case, and an accumulator are the three ingredients of nearly every type-level loop. With them you can map, filter, reverse, count, and join tuples entirely in the type system.

type MapToPairs<T extends unknown[], Acc extends unknown[] = []> =
  T extends [infer H, ...infer R]
    ? MapToPairs<R, [...Acc, [H, H]]>
    : Acc;

type A = MapToPairs<[1, 2]>; // [[1, 1], [2, 2]]

Quick Check

Test your understanding of the accumulator pattern.

Recap

You can now write loops at the type level.

  • Self-referential conditionals create recursion.
  • Split tuples into [H, ...R] and recurse on the tail.
  • A base case (empty tuple) stops the loop.
  • An accumulator builds the result and enables deep recursion.

Next: how conditionals behave specially over unions.

Frequently asked questions

Is the “Type-Level Recursion” lesson free?

Yes — the full text of “Type-Level Recursion” is free to read here on the web, and the TypeScript Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the TypeScript Academy course, upgrade to CoddyKit PRO.

What will I learn in “Type-Level Recursion”?

Loop over types using recursive conditional types. You practise TypeScript Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start TypeScript Academy?

No prior experience is required. TypeScript Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 3 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Type-Level Recursion” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this TypeScript Academy lesson?

Yes. Every TypeScript Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Types as a Computation Language
  2. Type-Level Conditionals
  3. Type-Level Recursion
  4. Distributive Conditional Types
← Back to TypeScript Academy