What are Generics in JavaScript and Why Are They Needed?
\n\nImagine you are writing a function that should work with different data types — numbers, strings, objects. Without Generics, you would either have to duplicate code or use any, losing control over types. Generics are a mechanism that allows you to create components (functions, classes, interfaces) that work with any data type while preserving type information.
Although JavaScript is a dynamically typed language, the Node.js ecosystem actively uses TypeScript, which adds static typing. It is in TypeScript that generics truly shine. However, understanding their principles is also useful for plain JavaScript — especially if you write libraries or work with modern frameworks.
\n\nIn this article, we will cover:
\n- \n
- Why Generics are needed \n
- How to declare and use generics in TypeScript \n
- Examples for Node.js (API, data processing) \n
- Limitations and best practices \n
Generics Basics: Syntax and Basic Examples
\n\nFirst Generic: The identity Function
\n\nA classic example is a function that returns what it receives as input:
\n\n// Without Generics\nfunction identity(value: any): any {\n return value;\n}\n\n// With Generics\nfunction identity<T>(value: T): T {\n return value;\n}\n\n// Usage\nconst num = identity<number>(42); // num — number\nconst str = identity<string>('hello'); // str — string\n\n\nThe symbol <T> is a type parameter. You can name it anything (T, U, Item), but tradition dictates using T (from Type).
Generics with Multiple Parameters
\n\nSometimes you need to work with multiple types simultaneously:
\n\nfunction pair<T, U>(first: T, second: U): [T, U] {\n return [first, second];\n}\n\nconst result = pair<number, string>(1, 'one');\n// result: [number, string]\n\n\nGenerics in Arrow Functions (Node.js)
\n\nIn React or utility functions, arrow functions with generics are often used:
\n\nconst wrapInArray = <T>(item: T): T[] => {\n return [item];\n};\n\nconst arr = wrapInArray('test'); // arr: string[]\n\n\nApplying Generics in Real Node.js Projects
\n\nAPI Handlers with Typed Responses
\n\nImagine you are writing a universal function for API requests. Without generics, you would lose data types:
\n\n// Bad: any\nasync function fetchData(url: string): Promise<any> {\n const response = await fetch(url);\n return response.json();\n}\n\n// Good: Generics\nasync function fetchData<T>(url: string): Promise<T> {\n const response = await fetch(url);\n return response.json();\n}\n\n// Usage\ninterface User {\n id: number;\n name: string;\n}\n\nconst user = await fetchData<User>('/api/user/1');\nconsole.log(user.name); // TypeScript knows that name is a string\n\n\nTyped Data Collections
\n\nWhen working with databases (e.g., via Prisma or Mongoose), generics help create universal repositories:
\n\nclass Repository<T extends { id: number }> {\n private items: T[] = [];\n\n add(item: T): void {\n this.items.push(item);\n }\n\n getById(id: number): T | undefined {\n return this.items.find(item => item.id === id);\n }\n}\n\n// Usage\ninterface Product {\n id: number;\n title: string;\n price: number;\n}\n\nconst productRepo = new Repository<Product>();\nproductRepo.add({ id: 1, title: 'Laptop', price: 1000 });\nconst laptop = productRepo.getById(1);\n\n\nWrappers for Error Handling
\n\nA universal function for