DEV Community

Cover image for Generics in typescript made easy
Akpevwe11
Akpevwe11

Posted on

Generics in typescript made easy

lets say in your house, you have different containers where you sort different things, like toys, books, and candies. Then you have a container that can hold any thing (whether it be books, candies, toys) without things getting messy.

Image description

Generics in Typescript is like that special box that can hold any thing. They are like magic placeholders that allow you to create a special box that can hold different types of things. Instead of specifying a specific type of thing, you can use a generic placeholder.

In a much more technical term, generics is just a "Yet to be defined" type, that obtains its' definition at the moment of use.

They allow you to create reusable components and functions that can work with a variety of data types while maintaining type safety. They provide a way to define placeholders for types that will be specified when the component or function is used.

To define a generic type in TypeScript, you use angle brackets (<>) and specify the generic parameter(s) inside them. Here's a basic example of a generic function:

function identity<T>(arg: T): T {
  return arg;
}

Enter fullscreen mode Exit fullscreen mode

In this example, T is the generic parameter representing the type of the argument and the return value. The function identity takes an argument of type T and returns a value of the same type T. When the function is called, the type inference system will automatically determine the specific type based on the argument passed.

lets look at another example that relates to our box analogy.

Imagine you have a function called getContents that takes a box and tells you what's inside it. With generics, you can write the function like this:

 function getContents<T>(box: T): T {
  return box;
}
Enter fullscreen mode Exit fullscreen mode

The <T> part is like a blank space where you can fill in any type you want when you use the function. For example, you can call the function like this:

 const toyBox: string = getContents("Teddy Bear");
const bookBox: number = getContents(5);
const candyBox: boolean = getContents(true);

Enter fullscreen mode Exit fullscreen mode

In this example, the T in getContents<T> becomes a specific type depending on what you pass to the function. The function returns the same type that you put in. So if you put a string, it will return a string. If you put a number, it will return a number.

Using generics with classes and interfaces.

Just as we can create generic functions that can work with a wide variety of types, we can as well create generic interfaces and classes.

A generic interface allows you to define an interface that can work with different types. It enables you to create reusable and flexible interfaces that can be used with various data types without explicitly specifying the type.

Here's an example of a generic interface in TypeScript:

interface Container<T> {
  data: T;
  add(item: T): void;
  remove(item: T): void;
  getSize(): number;
}

Enter fullscreen mode Exit fullscreen mode

In the above code, Container is a generic interface that takes a type parameter T. The interface has three methods:add, remove, andgetSize, which operate on the type T. The data property is also of type T.

You can then use this generic interface with different types when implementing a class, like this:

class NumberContainer implements Container<number> {
  data: number[] = [];

  add(item: number): void {
    this.data.push(item);
  }

  remove(item: number): void {
    const index = this.data.indexOf(item);
    if (index !== -1) {
      this.data.splice(index, 1);
    }
  }

  getSize(): number {
    return this.data.length;
  }
}

const container = new NumberContainer();
container.add(1);
container.add(2);
container.add(3);
console.log(container.getSize()); // Output: 3
container.remove(2);
console.log(container.getSize()); // Output: 2

Enter fullscreen mode Exit fullscreen mode

In the above example, the NumberContainer class implements the Container interface with the type parameter number. This allows the data property and methods to work specifically with numbers.

You can create additional implementations of the Container interface with different types, providing flexibility and reusability.

Why Do We Need Generics in Type Script?

Generics are important in TypeScript, for several reasons:

  1. Reusability: Generics allow you to create components, functions, and data structures that can work with different types of data. This promotes code reuse and reduces the need to write duplicate code for each specific type. By using generics, you can create versatile and flexible code that can be used with various data types.

  2. Type Safety: TypeScript is a statically typed language, and generics help enforce type safety. By using generics, you can specify the expected type of data that a component or function should work with. This helps catch type-related errors at compile-time, reducing the chances of runtime errors and improving the overall reliability of your code.

  3. Abstraction: Generics allow you to create abstract and generic algorithms or data structures. You can design components and functions that are independent of the specific data types they operate on, making your code more modular and easier to maintain. This level of abstraction enables you to write flexible and reusable code that can adapt to different scenarios.

  4. Code Readability: Generics can make your code more expressive and readable. When you use generics, you provide clear intentions and expectations about the types of data being used. This helps other developers understand the purpose and requirements of your code, making it easier to collaborate and maintain the codebase in the long run.

  5. Performance: Generics can help improve performance by avoiding unnecessary type conversions or type checking operations. By working directly with the specified types, you can optimize your code and reduce overhead, leading to more efficient execution.

Overall, generics provide a powerful tool for writing reusable, type-safe, and expressive code. They promote code modularity, improve maintainability, and enhance the overall quality and performance of your software projects.

Top comments (0)