Domina los tipos de utilidades de TypeScript para RR. HH.
Tiempo estimado de lectura: 4 min
- Ideas clave:
- Usa los Utility Types para derivar tipos desde una única fuente de verdad y evitar duplicación.
- Partial/Required/Readonly transforman la optionalidad e inmutabilidad de propiedades.
- Pick/Omit/Record y las utilidades de unión (Exclude/Extract/NonNullable) refinan shapes sin reescribir interfaces.
- ReturnType y Parameters facilitan mantener firmas coherentes entre capas.
Tipos de utilidades de TypeScript: una guía completa con ejemplos. Si escribes TypeScript habitualmente, dominar los Utility Types es una inversión que reduce deuda técnica y elimina duplicación. Aquí tienes una guía práctica, con ejemplos reales y criterio para decidir cuándo usar cada utilidad. Referencia oficial
Resumen rápido (lectores con prisa)
Qué es: Conjunto de tipos utilitarios para transformar y derivar tipos existentes sin reescribirlos.
Cuándo usarlo: Para evitar duplicación, expresar intención y derivar DTOs.
Por qué importa: Mantiene un único modelo como fuente de verdad y facilita refactors.
Cómo funciona: Aplica transformaciones a tipos (p. ej. Partial, Pick, ReturnType) para crear nuevos tipos derivados.
Transformaciones de propiedades: Partial, Required, Readonly
Partial
Partial: convierte todas las propiedades en opcionales. Ideal para operaciones PATCH o formularios de edición.
interface Usuario { id: number; nombre: string; email: string; }
type UsuarioPatch = Partial; // { id?: number; nombre?: string; email?: string }
Required
Required: fuerza todas las propiedades como obligatorias. Útil tras fusionar defaults.
interface Opts { timeout?: number; retries?: number; }
type OptsFull = Required; // timeout y retries obligatorios
Readonly
Readonly: marca todas las propiedades como inmutables a nivel de tipo.
type Estado = Readonly<{ id: number; name: string }>;
Criterio: usa Partial para inputs opcionales; Required cuando internamente necesitas garantías; Readonly para estado inmutable o contratos públicos que no deben mutarse.
Selección y exclusión de campos: Pick, Omit
Pick<Type, Keys>
Pick<Type, Keys>: crea un subtipo eligiendo propiedades concretas.
interface Producto { id: string; nombre: string; precio: number; costo: number; }
type ProductoCard = Pick<Producto, "id" | "nombre" | "precio">;
Omit<Type, Keys>
Omit<Type, Keys>: construye un tipo excluyendo propiedades.
type ProductoPublico = Omit<Producto, "costo">;
Criterio: Pick cuando seleccionas unas pocas propiedades; Omit cuando quieres el tipo completo menos unas cuantas.
Mapas y diccionarios: Record
Record<Keys, Type>: tipa objetos clave-valor. Mejor que usar { [k: string]: any }.
type Rol = "admin" | "user" | "guest";
const permisos: Record<Rol, boolean> = { admin: true, user: true, guest: false };
Criterio: usa Record cuando las claves son un union literal conocido. Para claves dinámicas complejas, considera Map o estructuras con validación.
Filtrado de uniones: Exclude, Extract, NonNullable
Exclude<Type, ExcludedUnion>
Exclude<Type, ExcludedUnion>: elimina miembros de una unión.
type Estado = "idle" | "loading" | "success" | "error";
type EstadoFinal = Exclude<Estado, "idle" | "loading">; // "success" | "error"
Extract<Type, Union>
Extract<Type, Union>: extrae los miembros que intersectan con otro union.
type Evento = string | number | (() => void);
type Callbacks = Extract<Evento, Function>; // () => void
NonNullable
NonNullable: elimina null y undefined.
type Res = string | null | undefined;
type ResNoNull = NonNullable<Res>; // string
Criterio: utiliza estas utilidades para refinar tipos en APIs donde algunas variantes no aplican o después de comprobaciones runtime.
Inferencia de funciones: ReturnType, Parameters
ReturnType
ReturnType: infiere el tipo de retorno de una función.
function crearConfig() { return { host: "db", port: 5432 }; }
type Config = ReturnType<typeof crearConfig>; // { host: string; port: number }
Parameters
Parameters: infiere la tupla de parámetros de una función.
function emitir(event: string, payload: unknown, priority = 0) {}
type EmitArgs = Parameters<typeof emitir>; // [string, unknown, number?]
Criterio: emplea ReturnType/Parameters para evitar replicar firmas en middlewares, factories o adaptadores. Mantén typeof para que el tipo sea dependiente del símbolo real.
Ejemplos combinados y patrones prácticos
DTOs y seguridad
Define entidad completa y deriva DTO público con Omit.
interface UserEntity { id: string; name: string; passwordHash: string; email: string; }
type UserPublic = Omit<UserEntity, "passwordHash">;
Actualizaciones y defaults
Recibe Partial del cliente, aplica defaults y convierte a Required internamente.
function normalize(cfg: Partial<Conf>): Required<Conf> {
return { timeout: cfg.timeout ?? 5000, retries: cfg.retries ?? 3 };
}
Mapeo de respuestas
Usa Record para indexar por IDs y ReturnType para inferir la forma del fetcher.
type FetchUsers = () => Promise<User[]>;
type Users = ReturnType<FetchUsers> extends Promise<infer U> ? U : never;
const byId: Record<string, Users[0]> = {};
Conclusión y criterio técnico
Los Utility Types son la palanca para escribir modelos que evolucionan sin romper todo. Reglas prácticas:
- Define un model central y deriva DTOs y shapes con utilidades.
- Prefiere
Pick/Omitsobre redefinir interfaces. - Mantén RxJS/logic fuera del tipado; usa
ReturnTypeyParameterspara conectar capas. - No abuses:
Partialen exceso puede esconder requisitos; valida en runtime cuando importe.
Lee la referencia oficial para casos avanzados y condicionales de tipos: capítulo de conditional types
Dominar estas piezas te deja escribir APIs más robustas y refactorizables. El siguiente paso: aplicar estos patrones en un modelo real de tu app y observar cómo desaparecen los tipos duplicados y las inconsistencias.
FAQ
- ¿Qué es un Utility Type en TypeScript?
- ¿Cuándo debo usar Partial?
- ¿Cuál es la diferencia entre Pick y Omit?
- ¿Para qué sirve Record?
- ¿Cómo ayudo ReturnType en arquitecturas en capas?
- ¿Qué cuidados tener con Partial y validación?
¿Qué es un Utility Type en TypeScript?
Un Utility Type es un tipo provisto por TypeScript que transforma tipos existentes sin reescribirlos, por ejemplo Partial, Pick, ReturnType. Se usan para derivar nuevos shapes a partir de una fuente de verdad.
¿Cuándo debo usar Partial?
Usa Partial para inputs opcionales como formularios de edición o endpoints PATCH donde no todos los campos son obligatorios. Evita abusar de Partial cuando la ausencia de campos puede ocultar requisitos críticos.
¿Cuál es la diferencia entre Pick y Omit?
Pick crea un subtipo seleccionando propiedades concretas; Omit crea un subtipo excluyendo propiedades. Usa Pick cuando quieres unas pocas propiedades y Omit para el tipo completo menos algunas.
¿Para qué sirve Record?
Record tipa objetos clave-valor con claves de un union literal conocido. Es útil para mapas indexados por roles o IDs cuando las claves son previsibles. Para claves dinámicas complejas considera Map o validación extra.
¿Cómo ayuda ReturnType en arquitecturas en capas?
ReturnType infiere automáticamente el tipo de retorno de una función, reduciendo duplicación de tipos entre capas (por ejemplo, fetchers y mappers). Se combina con condicionales (infer) para desestructurar tipos asíncronos.
¿Qué cuidados tener con Partial y validación?
Precaución: Partial puede ocultar requisitos importantes. Cuando la validez de los datos importa en runtime, aplica validación explícita y transforma Partial en un tipo requerido tras aplicar defaults o comprobaciones.
