Lección 90 — Constraints en Genéricos y Utility Types
🧠 Concepto
Section titled “🧠 Concepto”Los constraints (restricciones) limitan qué tipos puede aceptar un genérico. Los utility types son tipos predefinidos que transforman otros tipos de forma muy útil.
Constraints con extends
Section titled “Constraints con extends”Puedes restringir un genérico para que solo acepte tipos que cumplan cierta condición:
function longitud<T extends { length: number }>(item: T): number { return item.length;}
longitud('Hola'); // ✅ string tiene lengthlongitud([1, 2, 3]); // ✅ array tiene lengthlongitud(42); // ❌ number no tiene lengthkeyof T
Section titled “keyof T”keyof obtiene una unión de las claves de un tipo:
function obtenerPropiedad<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key];}
const usuario = { nombre: 'Ana', edad: 30 };obtenerPropiedad(usuario, 'nombre'); // ✅ tipo: stringobtenerPropiedad(usuario, 'email'); // ❌ ErrorUtility Types principales
Section titled “Utility Types principales”TypeScript incluye una serie de utility types globales que transforman tipos:
interface Usuario { id: number; nombre: string; email: string; activo: boolean;}Partial
Section titled “Partial”Todas las propiedades se vuelven opcionales:
type UsuarioParcial = Partial<Usuario>;// { id?: number; nombre?: string; email?: string; activo?: boolean }Required
Section titled “Required”Todas las propiedades se vuelven requeridas:
type UsuarioRequerido = Required<Partial<Usuario>>;Pick<T, K>
Section titled “Pick<T, K>”Selecciona solo las propiedades indicadas:
type UsuarioBasico = Pick<Usuario, 'id' | 'nombre'>;// { id: number; nombre: string }Omit<T, K>
Section titled “Omit<T, K>”Omite las propiedades indicadas:
type UsuarioSinEmail = Omit<Usuario, 'email'>;// { id: number; nombre: string; activo: boolean }Record<K, V>
Section titled “Record<K, V>”Crea un tipo con claves K y valores V:
type Pagina = Record<'inicio' | 'perfil' | 'admin', string>;// { inicio: string; perfil: string; admin: string }Exclude<T, U> y Extract<T, U>
Section titled “Exclude<T, U> y Extract<T, U>”type T1 = Exclude<'a' | 'b' | 'c', 'a'>; // 'b' | 'c'type T2 = Extract<'a' | 'b' | 'c', 'a' | 'c'>; // 'a' | 'c'NonNullable
Section titled “NonNullable”Elimina null y undefined:
type T3 = NonNullable<string | number | null | undefined>; // string | numberReturnType
Section titled “ReturnType”Obtiene el tipo de retorno de una función:
function crearUsuario() { return { id: 1, nombre: 'Ana' }; }type UsuarioType = ReturnType<typeof crearUsuario>;// { id: number; nombre: string }💻 Ejemplo
Section titled “💻 Ejemplo”// Constraint + keyoffunction obtenerValores<T, K extends keyof T>(obj: T, keys: K[]): T[K][] { return keys.map(key => obj[key]);}
const user = { id: 1, nombre: 'Ana', edad: 30 };const valores = obtenerValores(user, ['nombre', 'edad']); // tipo: (string | number)[]
// Utility types en accióninterface Producto { id: number; nombre: string; precio: number; descripcion?: string; categoria: string;}
// Actualización parcialfunction actualizarProducto(id: number, cambios: Partial<Producto>): void { // cambios puede tener cualquier subconjunto de propiedades}
actualizarProducto(1, { precio: 29.99 });actualizarProducto(2, { nombre: 'Nuevo nombre', descripcion: '...' });
// Mapa de configuracionestype ConfigApp = Record<string, string | number | boolean>;const config: ConfigApp = { url: 'https://api.example.com', puerto: 443, debug: true};
// Pick para DTOstype ProductoLista = Pick<Producto, 'id' | 'nombre' | 'precio'>;type ProductoDetalle = Omit<Producto, 'categoria'>;📝 Ejercicio
Section titled “📝 Ejercicio”Define una interfaz Tarea con: id, titulo, descripcion, completada, fechaLimite (opcional), etiquetas (array de strings). Luego crea:
- Un type
TareaCrearusandoOmitpara excluirid. - Un type
TareaResumenusandoPickpara soloid,titulo,completada. - Un
Record<string, Tarea>para un gestor de tareas por proyecto. - Una función genérica
actualizarTarea(id: number, cambios: Partial<Tarea>): void.
⚠️ Nota
Section titled “⚠️ Nota”Los utility types no mutan el tipo original. Crean un nuevo tipo derivado. El tipo base permanece intacto para otros usos.
Domina estos utility types: Partial, Pick, Omit y Record son los que más usarás. Ahorran toneladas de código repetitivo y hacen tus tipos más expresivos y mantenibles.