DEV Community

Cover image for Stop Writing Repetitive Code! Learn How TypeScript Generics Can Save You
Dipak Ahirav
Dipak Ahirav

Posted on

Stop Writing Repetitive Code! Learn How TypeScript Generics Can Save You

Table of Contents

please subscribe to my YouTube channel to support my channel and get more web development tutorials.


1. What are Generics?

Generics are a way to create reusable components in TypeScript that work with different types. They allow you to define the shape of a component while deferring the exact type until it is actually used.

This makes your code more flexible and reduces redundancy, allowing functions, classes, or interfaces to work with a variety of types while preserving type safety.


2. How Generics Work in TypeScript

Generics in TypeScript are essentially placeholders for types. Think of them as variables for types, much like how function parameters work. You define a generic parameter and use it in place of the actual type.

Here’s a simple example of a generic function:



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


Enter fullscreen mode Exit fullscreen mode

In this function, T is a generic type parameter. When the function is called, TypeScript will infer the type from the argument passed or you can explicitly pass the type.



// TypeScript infers the type
let output1 = identity(5);    // output1 is of type number
let output2 = identity("hello"); // output2 is of type string


Enter fullscreen mode Exit fullscreen mode

3. Benefits of Using Generics

Here’s why generics are so powerful in TypeScript:

  1. Reusability: Write code once, use it for multiple types.
  2. Type Safety: Generics help ensure that the correct types are used, reducing the risk of runtime errors.
  3. Flexibility: You can create more adaptable components that work across various types.

4. Generic Functions

Let’s expand on the idea of generic functions. You can use generics to make your functions more flexible without sacrificing type safety.

Example: A Generic Array Function

Imagine a function that returns the first element of an array. You want this function to work for any type of array—strings, numbers, objects, etc.



function getFirstElement<T>(arr: T[]): T {
  return arr[0];
}


Enter fullscreen mode Exit fullscreen mode

Here, the generic type T represents the type of the array’s elements, so TypeScript knows exactly what type to return.



let firstString = getFirstElement(["apple", "banana", "cherry"]); // TypeScript knows this is a string
let firstNumber = getFirstElement([10, 20, 30]); // TypeScript knows this is a number


Enter fullscreen mode Exit fullscreen mode

5. Generic Classes

Generics can also be applied to classes, allowing you to build reusable and type-safe classes.

Example: A Generic Stack Class

Here’s a classic example of a stack data structure implemented with generics:



class Stack<T> {
  private items: T[] = [];

  push(item: T): void {
    this.items.push(item);
  }

  pop(): T | undefined {
    return this.items.pop();
  }

  peek(): T | undefined {
    return this.items[this.items.length - 1];
  }
}


Enter fullscreen mode Exit fullscreen mode

Now, you can create stacks for any type:



const numberStack = new Stack<number>();
numberStack.push(10);
numberStack.push(20);
console.log(numberStack.peek()); // Output: 20

const stringStack = new Stack<string>();
stringStack.push("apple");
stringStack.push("banana");
console.log(stringStack.pop()); // Output: "banana"


Enter fullscreen mode Exit fullscreen mode

6. Generic Interfaces

Generics are also useful when defining interfaces. Let’s say you have an interface for a key-value pair, but you want it to work for any type of key and value.



interface KeyValuePair<K, V> {
  key: K;
  value: V;
}

const numberStringPair: KeyValuePair<number, string> = { key: 1, value: "apple" };
const stringBooleanPair: KeyValuePair<string, boolean> = { key: "isActive", value: true };


Enter fullscreen mode Exit fullscreen mode

With generics, the KeyValuePair interface can handle any combination of key-value types, giving you more flexibility in your code.


7. Constraints in Generics

Sometimes, you may want to limit the types that a generic can accept. This is where constraints come in. You can use constraints to specify that a type must extend a particular class or interface.

Example: A Function with a Constraint

Let’s say you want to create a generic function that works with objects that have a length property (e.g., arrays, strings). You can constrain the generic to ensure the type has the length property:



function logLength<T extends { length: number }>(item: T): void {
  console.log(item.length);
}

logLength("Hello");   // Output: 5
logLength([1, 2, 3]); // Output: 3
logLength({ length: 10, value: "example" }); // Output: 10


Enter fullscreen mode Exit fullscreen mode

In this example, the generic type T is constrained to only accept types that have a length property, ensuring type safety.


8. Practical Use Cases of Generics

Generics can be applied in many real-world scenarios to build reusable and safe code. Here are some common use cases:

1. Fetching Data with Type Safety

If you’re fetching data from an API and you want to ensure the correct type is returned:



async function fetchData<T>(url: string): Promise<T> {
  const response = await fetch(url);
  const data = await response.json();
  return data as T;
}

// Using the function
interface User {
  id: number;
  name: string;
}

const user = await fetchData<User>("https://api.example.com/user/1");
console.log(user.name); // TypeScript knows it's a string


Enter fullscreen mode Exit fullscreen mode

2. Reusable Form Validation

You can use generics to create reusable validation logic for forms with different types of fields:



interface FormField<T> {
  value: T;
  validate: () => boolean;
}

const textField: FormField<string> = {
  value: "Hello",
  validate: () => textField.value.length > 0,
};

const numberField: FormField<number> = {
  value: 42,
  validate: () => numberField.value > 0,
};


Enter fullscreen mode Exit fullscreen mode

9. Conclusion

Generics are a fundamental part of TypeScript that allow you to write more reusable, adaptable, and type-safe code. Whether you’re creating functions, classes, or interfaces, using generics ensures that your code works across different types without sacrificing the benefits of static typing.

By leveraging the power of generics, you’ll make your code more maintainable and versatile, enabling you to handle complex scenarios with ease. Try using generics in your next TypeScript project and see how they transform your development experience.


Call to Action:

Did you find this post helpful? Share it with your fellow developers, leave a comment, and don’t forget to follow me for more TypeScript insights!

Happy coding!


Start Your JavaScript Journey

If you're new to JavaScript or want a refresher, visit my blog on BuyMeACoffee to get started with the basics.

👉 Introduction to JavaScript: Your First Steps in Coding

Series Index

Part Title Link
1 Server-Side Rendering (SSR) in React with Next.js Read
2 Ditch Passwords: Add Facial Recognition to Your Website with FACEIO Read
3 The Ultimate Git Command Cheatsheet Read
4 Top 12 JavaScript Resources for Learning and Mastery Read
5 Angular vs. React: A Comprehensive Comparison Read
6 Top 10 JavaScript Best Practices for Writing Clean Code Read
7 Top 20 JavaScript Tricks and Tips for Every Developer 🚀 Read
8 8 Exciting New JavaScript Concepts You Need to Know Read
9 Top 7 Tips for Managing State in JavaScript Applications Read
10 🔒 Essential Node.js Security Best Practices Read
11 10 Best Practices for Optimizing Angular Performance Read
12 Top 10 React Performance Optimization Techniques Read
13 Top 15 JavaScript Projects to Boost Your Portfolio Read
14 6 Repositories To Master Node.js Read
15 Best 6 Repositories To Master Next.js Read
16 Top 5 JavaScript Libraries for Building Interactive UI Read
17 Top 3 JavaScript Concepts Every Developer Should Know Read
18 20 Ways to Improve Node.js Performance at Scale Read
19 Boost Your Node.js App Performance with Compression Middleware Read
20 Understanding Dijkstra's Algorithm: A Step-by-Step Guide Read
21 Understanding NPM and NVM: Essential Tools for Node.js Development Read

Follow and Subscribe:

Top comments (0)