Open In App

What is Recursive Generic in TypeScript ?

Last Updated : 14 Feb, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

In TypeScript, a recursive generic is a type that refers to itself within its definition, enabling the creation of data structures or types containing references to the same type within their structure. This capability proves invaluable when dealing with nested or hierarchical data structures like trees, linked lists or graphs. In this article, we’ll explore the syntax, various approaches, and examples of using recursive generics in TypeScript, with additional insights into their practical applications.

Using interfaces

Interfaces provide a way to define the structure of objects in TypeScript without implementing the details. They are useful for defining recursive generic types, particularly when describing the shape of a type.

Syntax:

interface ListNode<T> {
value: T;
next: ListNode<T> | null;
}

T: It is the type parameter that represents the type of the node value and the type of the next node. One can then use this type to create a linked list of any type, such as ListNode<number> or ListNode<string>.

Example: In the example below, ListNode<T> represents a node in a linked list, where T is the type of the node’s value and the type of the next node.

Javascript




interface ListNode<T> {
  value: T;
  next: ListNode<T> | null;
}
 
const list: ListNode<number> = {
  value: 1,
  next: {
    value: 2,
    next: {
      value: 3,
      next: null
    }
  }
};
 
console.log(list);


Output:

{ value: 1, next: { value: 2, next: { value: 3, next: null } } }

Example: A function that calculates the depth of a tree.

Javascript




interface Tree<T> {
  value: T;
  children: Array<Tree<T>>;
}
 
function depth<T>(tree: Tree<T>): number {
   
  if (tree.children.length === 0) {
    return 1;
  }
   
  return Math.max(...tree.children.map(child => depth(child))) + 1;
}
 
 
const tree: Tree<number> = {
  value: 1,
  children: [
    {
      value: 2,
      children: [
        {
          value: 3,
          children: []
        },
        {
          value: 4,
          children: []
        }
      ]
    },
    {
      value: 5,
      children: [
        {
          value: 6,
          children: []
        }
      ]
    }
  ]
};
 
 
console.log(depth(tree));


Output:

3

Using type aliases

Type aliases provide a way to give a type a name in TypeScript. They are another approach for defining recursive generic types, particularly useful when you want to create a new name for a type.

Syntax:

type Tree<T> = {
value: T;
children: Array<Tree<T>>;
}

Tree<T>: represents a tree node with a value of type T and an array of children nodes of the same type.

Example: In the example below, Tree<T> represents a tree node with a value of type T and an array of children nodes of the same type.

Javascript




type Tree<T> = {
  value: T;
  children: Array<Tree<T>>;
}
 
const tree: Tree<number> = {
  value: 1,
  children: [
    { value: 2, children: [{ value: 3, children: [] }] },
    { value: 4, children: [{ value: 5, children: [] }] }
  ]
};
 
console.log(tree);


Output

{ value: 1, children: [ { value: 2, children: [ { value: 3, children: [] } ] }, { value: 4, children: [ { value: 5, children: [] } ] } ] }

Example: A function that reverses a linked list

Javascript




type ListNode<T> = {
  value: T;
  next: ListNode<T> | null;
}
 
 
function reverse<T>(head: ListNode<T>): ListNode<T> {
   
  if (head === null || head.next === null) {
    return head;
  }
   
  const newHead = reverse(head.next);
  head.next.next = head;
  head.next = null;
  return newHead;
}
 
// Create a sample linked list
const list: ListNode<string> = {
  value: "a",
  next: {
    value: "b",
    next: {
      value: "c",
      next: null
    }
  }
};
 
 
console.log(reverse(list));


Output:

{   "value": "c",   "next": {     "value": "b",     "next": {       "value": "a",       "next": null     }   } } 

Using classes

Classes in TypeScript allow you to create objects with properties and methods. They are another approach for defining recursive generic types, suitable when you want to define the behavior and methods of a type along with its structure.

Syntax:

class BinaryTreeNode<T> {
value: T;
left: BinaryTreeNode<T> | null;
right: BinaryTreeNode<T> | null;
constructor(value: T, left: BinaryTreeNode<T> | null = null, right: BinaryTreeNode<T> | null = null) {
this.value = value;
this.left = left;
this.right = right;
}
}

Here, BinaryTreeNode<T> represents a binary tree node with a value of type T and two child nodes of the same type.

Example: In the example below, BinaryTreeNode<T> represents a binary tree node with a value of type T and two child nodes of the same type.

Javascript




class BinaryTreeNode<T> {
  value: T;
  left: BinaryTreeNode<T> | null;
  right: BinaryTreeNode<T> | null;
 
  constructor(value: T, left: BinaryTreeNode<T> | null = null, right: BinaryTreeNode<T> | null = null) {
    this.value = value;
    this.left = left;
    this.right = right;
  }
}
 
const binaryTree: BinaryTreeNode<string> = new BinaryTreeNode(
  "root",
  new BinaryTreeNode("left"),
  new BinaryTreeNode("right")
);
 
console.log(binaryTree);


Output :

BinaryTreeNode { value: 'root', left: BinaryTreeNode { value: 'left', left: null, right: null }, right: BinaryTreeNode { value: 'right', left: null, right: null } }


Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads