Day 51: Recursive Types
Tags: javascript, typescript, 100daysofcode
Published: 2023-09-19 14:00:00


### What Are Recursive Types? 🔄

Recursive types in TypeScript allow a type to refer to itself in its definition. This means you can create types that have nested structures, which can be infinitely deep. This concept is particularly handy when dealing with data structures like trees, linked lists, or JSON-like objects.

To illustrate this, let's start with a simple example of a binary tree:

```typescript
type TreeNode<T> = {
  value: T;
  left?: TreeNode<T>;
  right?: TreeNode<T>;
};
```


### Handling Recursive Types 🧩

Recursive types can sometimes pose challenges, especially when working with deeply nested data. TypeScript offers several techniques to navigate and manipulate these types effectively:

1. **Type Guards**: Use type guards like `typeof` and `instanceof` to ensure you're dealing with the right type at each level of recursion.

2. **Mapped Types**: Leverage mapped types to transform or modify recursive types to suit your needs.

3. **Utility Types**: TypeScript provides utility types like `Partial`, `Required`, and `Record` that can be helpful when working with recursive types.

4. **Conditional Types**: Use conditional types to apply different type logic based on conditions within the recursive structure.



## Examples

#### 1. Modeling Hierarchical Data 🌳

Imagine you're building a file system navigation component, and you want to represent the hierarchical structure of directories and files. Recursive types can help you model this structure effectively:

```typescript
type FileNode = {
  name: string;
  isFile: boolean;
  children?: FileNode[];
};
```

### 2. Parsing JSON Data 🌐

When working with JSON data from external APIs, you often need to parse deeply nested structures. Recursive types make it easier to define the shape of the data you expect:

```typescript
type JsonValue = string | number | boolean | null | JsonObject | JsonArray;
type JsonObject = { [key: string]: JsonValue };
type JsonArray = JsonValue[];
```

### 3. Implementing Tree Data Structures 🌲

If you're working on algorithms or data structures that involve trees (e.g., binary trees, AVL trees, or B-trees), recursive types are essential for defining the nodes and structures:

```typescript
type BinaryTreeNode<T> = {
  value: T;
  left?: BinaryTreeNode<T>;
  right?: BinaryTreeNode<T>;
};
```


### [Advanced examples with template literal](https://dev.to/dhrn/day-50-template-literal-types-56hd)

#### 1. Uppercase a String

You can create a type that transforms a string into uppercase:

```typescript
type Uppercase<S extends string> = S extends `${infer L}` ? Uppercase<L> : S;

type UppercasedGreeting = Uppercase<"hello">; // UppercasedGreeting = "HELLO"
```

#### 2. Capitalize the First Letter

Create a type that capitalizes the first letter of a string:

```typescript
type CapitalizeFirstLetter<S extends string> = S extends `${infer First}${infer Rest}`
  ? `${Uppercase<First>}${Rest}`
  : S;

type CapitalizedGreeting = CapitalizeFirstLetter<"hello">; // CapitalizedGreeting = "Hello"
```

#### 3. Replace Substrings

Build a type that replaces all occurrences of a substring within a string:

```typescript
type Replace<S extends string, From extends string, To extends string> = S extends `${infer Prefix}${From}${infer Suffix}`
  ? `${Prefix}${To}${Replace<Suffix, From, To>}`
  : S;

type ReplacedText = Replace<"hello world", "world", "universe">; // ReplacedText = "hello universe"
```

#### 4. Remove Spaces

Create a type that removes all spaces from a string:

```typescript
type RemoveSpaces<S extends string> = S extends `${infer Left} ${infer Right}`
  ? RemoveSpaces<`${Left}${Right}`>
  : S;

type NoSpacesText = RemoveSpaces<"hello world">; // NoSpacesText = "helloworld"
```

#### 5. Reverse a String

Build a type that reverses a string:

```typescript
type ReverseString<S extends string> = S extends `${infer First}${infer Rest}`
  ? `${ReverseString<Rest>}${First}`
  : S;

type ReversedGreeting = ReverseString<"hello">; // ReversedGreeting = "olleh"
```

### Pitfalls and Considerations 🕳️

While recursive types are a powerful tool, they come with some considerations:

1. **Infinite Types**: Be cautious of creating types that could potentially become infinite. TypeScript will eventually enforce a recursion limit.

2. **Performance**: Deeply nested recursive types can impact performance, so it's essential to balance type safety with practicality.

3. **Readability**: Overly complex recursive types can make your code harder to understand. Use comments and clear type names to enhance readability.