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.
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álidointerface 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álidoRequired<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.
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; }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.
// Un Usuario sin la contraseña
type UsuarioPublico = Omit<Usuario, "password">;// 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.
// 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
};// 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.
// --- 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!// --- 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:
Partialpara definir una variableupdate.Pickpara definir una variablepreviewque solo tenga el título.Recordpara crear unCatalogodonde 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.