Saltar al contenido principal
Volver atrás

TypeScript #10: Mezclando Conceptos (Generics + Interfaces)

#typescript #generics #interfaces

La parte difícil: combinar Generics, Interfaces y Tipos sin volverse loco.

Aquí es donde mucha gente se pierde. Empezamos a ver <T>, interface, type, return T… todo mezclado y parece jeroglífico.

Vamos a desenredarlo paso a paso.

Generics en Interfaces vs Tipos

¿Cuándo usar uno u otro? La sintaxis es casi idéntica.

typescript
// Opción A: Interface Genérica
interface CajaInterface<T> {
  valor: T;
}

// Opción B: Type Genérico
type CajaType<T> = {
  valor: T;
};

// Se usan IGUAL
const a: CajaInterface<string> = { valor: "Hola" };
const b: CajaType<string> = { valor: "Hola" };

No hay diferencia funcional real para datos simples. Usemos la que prefiramos (solemos usar interface para objetos y type para funciones/uniones).

El Patrón “Wrapper” (Datawrapper)

Este es el caso de uso #1 en el mundo real: Respuestas de API. Tenemos una estructura fija (data, status, error) pero el contenido de data cambia según la llamada.

typescript
// Definamos la "cáscara" genérica
interface ApiResponse<Data> {
  status: number;
  message: string;
  data: Data; // 👈 Aquí va la magia
}

// Definamos nuestros modelos concretos
interface User { id: number; name: string; }
interface Product { sku: string; price: number; }

// ¡Combinémoslos!
type UserResponse = ApiResponse<User>;
type ProductResponse = ApiResponse<Product>;

function fetchUser(): UserResponse {
  return {
      status: 200,
      message: "OK",
      data: { id: 1, name: "BartoloDev" } // TS sabe que data es User
  };
}

”Prop Drilling” de Tipos

A veces tenemos Generics dentro de Generics. Es como pasar una variable de un abuelo a un nieto.

typescript
interface PaginatedResponse<T> {
  items: T[];       // Array de T
  total: number;
  page: number;
}

// ApiResponse contiene PaginatedResponse, que contiene User
type UsersApi = ApiResponse<PaginatedResponse<User>>;

// Parece complejo, pero TS lo resuelve solo:
// UsersApi = { 
//   status: number,
//   message: string,
//   data: { 
//      items: User[], 
//      total: number,
//      page: number,
//   } 
// }

Generics en Funciones Flecha

La sintaxis aquí puede llegar a asustar, especialmente en archivos .tsx (React) y donde se vuelve más complejo.

typescript
// Función normal
function identity<T>(arg: T): T { return arg; }

// Función flecha
const identityArrow = <T>(arg: T): T => arg;

// ⚠️ En archivos .tsx (React), necesitamos una coma para que no crea que es HTML
const hookEnReact = <T,>(arg: T) => { ... };

¡Probémoslo!

Creemos una interfaz Respuesta<T> con datos: T y error: string | null. Luego creemos un objeto que use esa interfaz con number.

En la Parte 9, vamos a relajarnos un poco y ver los Utility Types (Partial, Pick, Omit). Son tipos genéricos que TS ya trae de serie para hacernos la vida más fácil.