Guide to TypeScript Recursive Type

Recursive types in TypeScript refer to types that reference themselves. These types can be extremely useful when modeling data structures like linked lists, trees, and other hierarchical structures. This guide will explore the nuances of recursive types in TypeScript and provide examples of their usage.

Basic Recursive Types

A recursive type in TypeScript is a type that references itself. Consider the classic example of a linked list:

interface LinkedList { value: number; next: LinkedList | null; }

In this example, the LinkedList type references itself within the next property. This allows you to represent an infinitely long linked list in your TypeScript code.

Use Cases of Recursive Types

1. Trees

Binary trees are a common data structure where each node has a value and two child nodes (left and right):

interface BinaryTreeNode { value: number; left: BinaryTreeNode | null; right: BinaryTreeNode | null; }

2. Hierarchical Structures

Consider a folder structure where each folder can have many subfolders:

interface Folder { name: string; subFolders: Folder[]; }

3. JSON-like Data

To model a data structure where a key can have a string or another object:

type JSONValue = string | number | boolean | JSONObject; interface JSONObject { [key: string]: JSONValue; }

Conditional Recursive Types

TypeScript allows you to conditionally define recursive types using ternary operators. These can be especially useful for more complex structures or parsing tasks:

type NestedArray<T> = T[] | NestedArray<T>[]; const example: NestedArray<number> = [1, [2, 3], [[4, 5], [6, 7]]];

Here, NestedArray<T> can be an array of type T or an array of NestedArray<T> itself, allowing for deeply nested arrays.

Limitations and Workarounds

1. Direct Recursion

TypeScript doesn't always handle direct recursion smoothly. This is especially true for more complex types. For instance, if you try to infer the type of a recursive function, you might encounter issues.

Workaround: Often, using type annotations or breaking the recursion with type aliases can help.

2. Maximum call stack size exceeded

When working with recursive data structures, there's a risk of exceeding the maximum call stack size if you're not careful, especially during recursive operations. This is a runtime error and not directly a TypeScript issue, but it's something to be aware of.

Workaround: Depending on the scenario, iterative solutions or tail recursion optimizations might be useful.

3. Excessive Depth

While TypeScript is powerful, it's not always easy to define infinitely deep structures. At some point, you may encounter type depth issues.

Workaround: In such cases, consider limiting the depth or making your type definitions more general.


By understanding and leveraging recursive types in TypeScript, you can model complex data structures more effectively and ensure type safety throughout your applications.

Invite only

We're building the next generation of data visualization.