Generics

Generic types are like functions for type declarations the let you pass some arguments (other types) later. This is a (part of) simple linked list implementation:

type ListNode = {
  value: any;
  next: ListNode|null;
}

function add(head: ListNode, value: any): ListNode {
  const newElement: ListNode = { value, next: null };
  let current: ListNode = head;

  while (current.next) {
      current = current.next;
  }

  current.next = newElement;

  return current;
}

This code is fine, it works, but gives no type safety:

const head: ListNode = {
  value: 12,
  next: null
}

add(head, "Hello!")

Generics give us a way to specify: this is a LinkedList of numbers. Or strings. Or whatever.

A generic type

This is the same type, but this time written as generic:

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

T is the variable type argument here. When creating the head element we'll specify the type of values in the list:

const head: ListNode<number> = {
    value: 12, next: null
}

Trying to give different type of value will result in an error:

const head: ListNode<number> = {
    value: "12", next: null
}

Generic functions

Functions can be generic too! First, let's take a look at a simple example:

function makeTuple<T>(a: T, b: T): [T, T] {
    return [a, b];
}

Great thing about generic functions is that TypeScript is actually quite smart about them. This obviously works as expected:

makeTuple<number>(1, 20);

But TypeScript is also to guess the type T based on the first argument it gets.

makeTuple("Hello", "Devmeetings")
makeTyple("Hello", 13) // error

Let's rewrite the add function from previous example:

function add<T>(head: ListNode<T>, value: T): ListNode<T> {
  const newElement: ListNode<T> = { value, next: null };
  let current: ListNode<T> = head;

  while (current.next) {
      current = current.next;
  }

  current.next = newElement;

  return current;
}


add(head, 12);
add(head, "That's illegal!"); // error

Resources

Last updated