Saltar al contenido principal
Volver atrás

TypeScript #11: Utility Types (Tus mejores amigos)

#typescript #utility-types #advanced

Partial, Pick, Omit... Herramientas que TS te regala para transformar tipos sin tener que reescribirlos.

TypeScript viene con una “navaja suiza” de tipos predefinidos llamados Utility Types. Son Generics que toman un tipo y nos devuelven una versión modificada.

Partial<T> (Todo opcional)

Imaginemos que tenemos un usuario completo, pero para una actualización (PATCH), solo envíamos los campos que cambiaron.

typescript
interface Usuario {
  id: number;
  nombre: string;
  email: string;
}

function actualizarUsuario(id: number, cambios: Partial<Usuario>) {
  // 'cambios' ahora es como si todas las props tuvieran '?'
  // { id?: number; nombre?: string; email?: string; }
  ...
}

actualizarUsuario(1, { nombre: "BartoloDev" }); // ✅ Válido

Required<T> (Todo obligatorio)

Lo contrario a Partial. Quita todos los ? de una interfaz.

Pick<T, Keys> (Elige lo que quieres)

A veces tenemos una interfaz gigante y solo necesitamos un par de campos para un componente pequeño.

typescript
interface Producto {
  id: number;
  nombre: string;
  precio: number;
  descripcion: string;
  stock: number;
  ...
}

// Creamos un nuevo tipo solo con nombre y precio
type EtiquetaProducto = Pick<Producto, "nombre" | "precio">;
// { nombre: string; precio: number; }

Omit<T, Keys> (Quita lo que NO quieres)

Lo contrario a Pick. Elimina campos específicos. Muy útil para quitar datos sensibles o innecesarios.

typescript
// Un Usuario sin la contraseña
type UsuarioPublico = Omit<Usuario, "password">;

Record<Keys, Type> (Diccionarios)

Este es un poco especial. Sirve para crear objetos donde no sabemos el nombre de las propiedades, pero sabemos qué valores tendrán.

Piensa en una Agenda Telefónica:

  • Las Claves (Keys) son nombres (Strings).
  • Los Valores (Type) son números (Numbers).

No sabemos si guardarás a “Pepe” o “María”, pero sabemos que la estructura siempre será Nombre -> Numero.

typescript
// Un objeto normal requiere saber las claves de antemano
// interface Agenda { pepe: number } // ❌ Poco práctico
// Con Record, definimos el "molde" de la clave y el valor
type Agenda = Record<string, number>;

const misContactos: Agenda = {
  "Pepe": 666555444,
  "Maria": 612312312,
  // "Juan": "hola" // ❌ Error: el valor debe ser number
};
¿Por qué no usar Map?

En JavaScript moderno existe Map, que es un diccionario real. En TypeScript sería Map<string, number>.

Sin embargo, usamos Record (Objetos) el 90% del tiempo porque los Objetos se convierten a JSON automáticamente, mientras que los Map se pierden al enviarlos a una API.

typescript
// --- Opción A: Record (Objeto de toda la vida) ---
// Ideal para guardar en Base de Datos o enviar a Frontend
type AgendaRecord = Record<string, number>;

const agenda1: AgendaRecord = {
  "Pepe": 666555444
};

// --- Opción B: Map (La forma "Pro") ---
// Ideal para lógica interna compleja (sets, gets, size...)
type AgendaMap = Map<string, number>;

const agenda2: AgendaMap = new Map();
agenda2.set("Pepe", 666555444); // ✅ TS te vigila que metas number
// agenda2.set("Juan", "hola"); // ❌ Error!

¡Probémoslo!

Tenemos una interfaz Todo. Usemos:

  1. Partial para definir una variable update.
  2. Pick para definir una variable preview que solo tenga el título.
  3. Record para crear un Catalogo donde guardemos Todo por su ID (string).

En la Parte 10, entraremos en territorio avanzado. Usaremos infer para que TypeScript “extraiga” tipos de lugares imposibles, como el tipo de retorno de una función.