⚡ JavaScript

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?

  1. <T> — we declared a type parameter T. It's like a variable, but for types.
  2. arr: T[] — an array of elements of type T.
  3. : T — the function returns a value of type T.
  4. 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