Saltar al contenido principal
Volver atrás

TypeScript #8: Generics (Lo básico)

#typescript #generics #basics

La herramienta más potente de TS. Aprende a escribir código reutilizable y flexible con <T>.

Hasta ahora, nuestros tipos eran fijos. string es siempre string. Pero, ¿y si queremos crear una función o un componente que funcione con cualquier tipo, pero sin perder la seguridad?

Ahí entran los Generics. Son como “parámetros” pero para Tipos.

El Problema: Duplicación o any

Imaginemos una función que devuelve lo que le pasas (clásica función identity).

typescript
// Opción 1: Duplicar código para cada tipo
function returnString(val: string): string { return val; }
function returnNumber(val: number): number { return val; }

// Opción 2: Usar 'any' (Perdemos el tipo)
function returnAny(val: any): any { return val; }

const resultado = returnAny("Hola"); 
// TS no sabe que 'resultado' es string, cree que es 'any'.
// resultado.toUpperCase(); // ❌ No autocompleta

La Solución: Generics <T>

Podemos decirle a la función: “Oye, vamos a pasarte un tipo al que llamaremos T. Úsalo para tipar el argumento y el retorno”.

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

// Uso explícito (le pasamos el tipo)
const num = identity<number>(123); // num es number

// Inferencia (TS es listo y lo adivina)
const str = identity("Hola"); // str es "Hola" (literal) o string

La T es una convención (viene de Type), pero podemos llamarlo como queramos: <Data>, <Response>, <Props>.

Interfaces Genéricas

Esto es súper común en React o respuestas de API.

typescript
// Una caja que puede contener cualquier COSA
interface Caja<T> {
  contenido: T;
  etiqueta: string;
}

const cajaDeZapatos: Caja<string> = {
  contenido: "Nike Air",
  etiqueta: "Deportes"
};

const cajaDeRegalo: Caja<number> = {
  contenido: 1000,
  etiqueta: "Dinero"
};

Ejemplo Real: useState

Si hemos usado React, ya hemos usado Generics sin saberlo.

tsx
// useState es una función Genérica: function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>]

// Inferencia:
const [name, setName] = useState("Alice"); // TS infiere que T es string

// Explícito (útil cuando el valor inicial es null):
const [user, setUser] = useState<Usuario | null>(null);

Uniones vs Generics

Igual has pensado: “¿Y si uso string | number en vez de <T>?”

function echo(val: string | number): string | number { return val; }
const res = echo("Hola"); // TS cree que 'res' es 'string | number' ❌
// Perdiste la precisión: TS ya no sabe que es un string.

Con <T>, TS mantiene la conexión: entró string -> sale string.


¡Probémoslo!

Crea una función envolver que metas lo que metas, te lo devuelva dentro de un array. Si metes 10, sale [10]. Si metes "hola", sale ["hola"].

En la Parte 7, subiremos el nivel. Veremos cómo restringir esos Generics (para que no valga “cualquier cosa”) y cómo darles valores por defecto.