Genéricos e Tipos Paramétricos em JavaScript (Node.js): Um Guia Detalhado
What is it and why? (a real-life analogy)
Imagine you work in a toy warehouse. You have three boxes:
- Box for cars — you can only put cars in it.
- Box for dolls — only for dolls.
- Box for construction sets — only for construction sets.
Every time you want to put something new away, you need to run and get a new box. That's inconvenient, right?
Now imagine a universal box. It's not labeled "for cars" or "for dolls." It simply says: "For toys." You can put a car in it, then a doll, then a construction set — and the box will work just as well. But there is one rule: if you decide that this box will only contain cars, you can no longer put a doll in it — the box will "remember" your choice.
In programming, such "universal boxes" are called generics (from the English word generic — general, generic). And the "type of toy" that we specify when creating the box is called a parametric type.
💡 Important note: In JavaScript (especially Node.js), generics didn't appear right away. For a long time, JS was "dynamic" — you could put anything in a box, and no one checked. But with the advent of TypeScript (a superset of JS) and modern development tools, generics became necessary for writing reliable code.
Why is this needed?
- Type safety: If you say "this box is only for cars," the compiler won't let you accidentally put a doll in it.
- Code reuse: One function can work with numbers, strings, objects — you don't need to write three different functions.
- Readability: Other programmers can immediately see what data types are expected.
Theory: how it works internally
Let's look "under the hood" and understand what happens when we use generics.
What is a "type" in programming?
A type is a set of rules that describe what can be done with data. Numbers can be added, strings can be concatenated, arrays can be iterated. When we say "this variable is a number," we promise the computer: "I will only work with it as a number."
The problem without generics
Imagine a function that returns the first element of an array:
// Without generics — you have to use any
function getFirstElement(arr) {
return arr[0];
}
const numbers = [1, 2, 3];
const firstNumber = getFirstElement(numbers); // Type: any — we lost the information!
// Now we can accidentally do:
const length = firstNumber.length; // Error! Numbers don't have length, but the compiler is silent
The problem is that we lost the type information. The computer no longer knows if there was a car or a doll inside — it just sees "something."
How generics solve this problem
Generics work like variables for types. Instead of writing a specific type (number, string), we write a type parameter — usually denoted by letters T, U, V or meaningful words.
// With generics — the type is preserved!
function getFirstElement<T>(arr: T[]): T {
return arr[0];
}
const numbers = [1, 2, 3];
const firstNumber = getFirstElement(numbers); // Type: number — information preserved!
// Now the compiler won't let us do:
const length = firstNumber.length; // ❌ Error! Property 'length' does not exist on type 'number'
What happened here?
<T>— we declared a type parameter T. It's like a variable, but for types.arr: T[]— an array of elements of type T.: T— the function returns a value of type T.- When we called the function with an array of numbers, TypeScript automatically substituted T = number.
How it looks in memory
When you write const box = new Box<number>(), the compiler creates a "snapshot" of the class where all T's are replaced with number. This is called monomorphization. In JavaScript (via TypeScript), this doesn't happen directly — TS just checks types at compile time, and at runtime JS works as usual.
🔧 Technical detail: Unlike Java or C#, where generics exist at runtime as well, in TypeScript they are completely erased after compilation. This is called "type erasure." That is, generics are only for checking at the code-writing stage; they don't exist in the final JS.
Syntax in JavaScript (Node.js): all forms and variations
It's important to understand: pure JavaScript does not support generics. They only exist in TypeScript, which compiles to JS. But since a modern Node.js project without TypeScript is rare, we will look at TypeScript syntax.
1. Basic form with one parameter
// The simplest case — one type parameter T
function identity<T>(value: T): T {
return value;
}
// Usage:
const num = identity<number>(42); // num: number
const str = identity<string>("hello"); // str: string
// You can omit the type — TS will infer it:
const auto = identity(true); // auto: boolean
2. Multiple type parameters
// When you need to work with two different types
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
const result = pair<number, string>(1, "one");
// result: [number, string]
3. Constraints
// We can say: "T must