Generics in JavaScript: why they are needed and how to use them in practice

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Why does JavaScript need Generics?



Development with JavaScript (especially in the Node.js environment) often faces the problem of code duplication. Imagine you need to write a function that works with numbers, strings, and arrays. Without Generics, you either write several overloads or use any, losing all the magic of types.



Generics is a mechanism that allows you to create components (functions, classes, interfaces) that work with different data types while maintaining strict typing. In the context of JavaScript, generics are fully available through TypeScript, but understanding their principles is critical for any modern developer.



In this article, we will break down why generics are needed, how to use them in practice in Node.js, and why this makes your code safer and more reusable.



1. Generics in TypeScript: The Foundation of Type Safety



Although pure JavaScript does not have built-in support for generics, TypeScript (a superset of JS) provides them in full. This is why all modern frameworks and libraries for Node.js (NestJS, Express with types) actively use them.



Simple example: the identity function



The classic example is a function that returns what it receives:



// Without generics — using any (losing the type)function identity(arg: any): any {  return arg;}

// With generic — the type is preservedfunction identity<T>(arg: T): T { return arg;}

// Usageconst num = identity<number>(42); // Type: numberconst str = identity<string>('hello'); // Type: string


Note: <T> is a type parameter. We tell TypeScript: "The type will be passed later." The compiler automatically infers the type, so you can often just write identity(42).



Why is this important for Node.js?



In Node.js, you constantly work with APIs, databases, and external services. Generics allow you to guarantee that data arrives in exactly the format you expect:



// Function for working with an APIasync function fetchData<T>(url: string): Promise<T> {  const response = await fetch(url);  return response.json() as Promise<T>;}

// Now we know the exact structure of the responseinterface User { id: number; name: string;}

const user = await fetchData<User>('/api/user/1');// user.name — string, user.id — number (TypeScript checks this)


2. Practical Scenarios: Working with Arrays and Collections



Generics are especially useful when working with arrays and data collections. Let's look at a real example from Node.js — a filtering and transformation function.



Generic function for mapping



// Function transforms an array of one type into an array of another typefunction mapArray<T, U>(items: T[], mapper: (item: T) => U): U[] {  return items.map(mapper);}

// Example: get only names from Userinterface User { id: number; name: string; email: string;}

const users: User[] = [ { id: 1, name: 'Alice', email: 'alice@example.com' }, { id: 2, name: 'Bob', email: 'bob@example.com' }];

const names: string[] = mapArray(users, (user) => user.name);// names: ['Alice', 'Bob'] — type string[] is guaranteed


Here <T, U> are two type parameters. T is the type of the input array, U is the type of the result. This provides maximum flexibility without losing types.



Generic class for a stack



In Node.js, data structures are often needed. Let's create a type-safe stack:



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

pu

Blogs

Book Recommendations