En esta página
Utility types
Los utility types: transformaciones de tipos listas para usar
TypeScript incluye en su librería estándar un conjunto de utility types (tipos utilitarios) que transforman tipos existentes de maneras comunes. En lugar de reescribir variantes de tus interfaces para cada caso de uso, los utility types te permiten derivarlas de forma declarativa y mantenible.
Conocerlos bien te ahorrará horas de trabajo y mantendrá tu código DRY (Don't Repeat Yourself) en la capa de tipos.
Partial\
Convierte todas las propiedades de T en opcionales:
interface Configuracion {
tema: "claro" | "oscuro";
idioma: string;
notificaciones: boolean;
itemsPorPagina: number;
}
type ConfiguracionParcial = Partial<Configuracion>;
// Equivale a:
// {
// tema?: "claro" | "oscuro";
// idioma?: string;
// notificaciones?: boolean;
// itemsPorPagina?: number;
// }
function actualizarConfiguracion(
actual: Configuracion,
cambios: Partial<Configuracion>
): Configuracion {
return { ...actual, ...cambios };
}
const configBase: Configuracion = {
tema: "oscuro",
idioma: "es",
notificaciones: true,
itemsPorPagina: 20,
};
const nuevaConfig = actualizarConfiguracion(configBase, { tema: "claro" });Required\
Hace que todas las propiedades opcionales se vuelvan obligatorias. Es el inverso de Partial:
interface OpcionesConexion {
host?: string;
puerto?: number;
ssl?: boolean;
timeout?: number;
}
type ConexionResuelta = Required<OpcionesConexion>;
// Todas las propiedades son ahora obligatorias
function conectar(opciones: Required<OpcionesConexion>): string {
const protocolo = opciones.ssl ? "https" : "http";
return `${protocolo}://${opciones.host}:${opciones.puerto}`;
}
const defaults: Required<OpcionesConexion> = {
host: "localhost",
puerto: 5432,
ssl: false,
timeout: 5000,
};Readonly\
Hace que todas las propiedades de T sean de solo lectura (no se pueden reasignar):
interface Punto {
x: number;
y: number;
}
function reflejar(punto: Readonly<Punto>): Punto {
// punto.x = 0; // ❌ Error: no se puede asignar a propiedad de solo lectura
return { x: -punto.x, y: -punto.y };
}
const origen: Readonly<Punto> = { x: 0, y: 0 };Readonly es útil para marcar parámetros de funciones que no deben ser mutados, comunicando intención y permitiendo que TypeScript lo verifique.
Pick\
Crea un nuevo tipo seleccionando solo las propiedades K de T:
interface Articulo {
id: string;
titulo: string;
contenido: string;
autor: string;
etiquetas: string[];
vistas: number;
publicadoEn: Date;
}
// Solo los campos necesarios para una tarjeta de vista previa
type PreviewArticulo = Pick<Articulo, "id" | "titulo" | "autor" | "publicadoEn">;
// { id: string; titulo: string; autor: string; publicadoEn: Date }Omit\
El complemento de Pick: crea un tipo con todas las propiedades de T excepto las indicadas en K:
// En operaciones de creación, el id lo genera el servidor
type CrearArticuloDto = Omit<Articulo, "id" | "vistas" | "publicadoEn">;
// { titulo: string; contenido: string; autor: string; etiquetas: string[] }Record\
Crea un tipo de objeto con claves de tipo K y valores de tipo V:
type Estado = "pendiente" | "activo" | "inactivo" | "eliminado";
type DescripcionEstados = Record<Estado, string>;
const descripciones: DescripcionEstados = {
pendiente: "Esperando aprobación",
activo: "Operativo y visible",
inactivo: "Temporalmente desactivado",
eliminado: "Marcado para eliminación",
};
// También útil para cachés y mapas
type Cache<T> = Record<string, T>;
const cacheUsuarios: Cache<{ nombre: string; email: string }> = {};Extract\ y Exclude\
Extract mantiene solo los tipos de T que son asignables a U. Exclude los elimina:
type Primitivos = string | number | boolean | null | undefined;
type SoloCadenas = Extract<Primitivos, string>; // string
type SinNulables = Exclude<Primitivos, null | undefined>; // string | number | boolean
type Evento = "click" | "focus" | "blur" | "change" | "submit";
type EventosTeclado = "keydown" | "keyup" | "keypress";
type EventosTodos = Evento | EventosTeclado;
type EventosSoloRaton = Exclude<EventosTodos, EventosTeclado>;
// "click" | "focus" | "blur" | "change" | "submit"NonNullable\
Elimina null y undefined de un tipo:
type ValorNulable = string | number | null | undefined;
type ValorSeguro = NonNullable<ValorNulable>; // string | number
function procesarValor(valor: string | null | undefined): string {
const seguro: NonNullable<typeof valor> = valor ?? "";
return seguro.toUpperCase();
}ReturnType\ y Parameters\
Extraen el tipo de retorno y los parámetros de una función, respectivamente:
function buscarUsuario(id: number, incluirPerfil: boolean) {
return {
id,
nombre: "Ejemplo",
perfil: incluirPerfil ? { bio: "..." } : null,
};
}
type ResultadoBusqueda = ReturnType<typeof buscarUsuario>;
// { id: number; nombre: string; perfil: { bio: string } | null }
type ParamsBusqueda = Parameters<typeof buscarUsuario>;
// [id: number, incluirPerfil: boolean]
// Útil para wrappers y proxies
function buscarConLog(...args: Parameters<typeof buscarUsuario>): ReturnType<typeof buscarUsuario> {
console.log("Buscando usuario con args:", args);
return buscarUsuario(...args);
}ConstructorParameters\
Similar a Parameters, pero para constructores de clases:
class ConexionDB {
constructor(
private host: string,
private puerto: number,
private ssl: boolean
) {}
}
type ArgsConexion = ConstructorParameters<typeof ConexionDB>;
// [host: string, puerto: number, ssl: boolean]
function crearConexion(...args: ConstructorParameters<typeof ConexionDB>): ConexionDB {
return new ConexionDB(...args);
}Awaited\
Resuelve recursivamente el tipo de una Promise (o cualquier thenable):
async function obtenerDatos(): Promise<string[]> {
return ["dato1", "dato2"];
}
type Resultado = Awaited<ReturnType<typeof obtenerDatos>>;
// string[]
// Casos anidados:
type PromesaAnidada = Awaited<Promise<Promise<number>>>;
// numberEjemplo completo: servicio CRUD con utility types
// ──────────────────────────────────────────────────────────────
// Entidad base
// ──────────────────────────────────────────────────────────────
interface Tarea {
id: string;
titulo: string;
descripcion: string;
completada: boolean;
prioridad: "baja" | "media" | "alta";
etiquetas: string[];
creadaEn: Date;
actualizadaEn: Date;
}
// ──────────────────────────────────────────────────────────────
// DTOs derivados — sin duplicar propiedades
// ──────────────────────────────────────────────────────────────
type CrearTareaDto = Omit<Tarea, "id" | "creadaEn" | "actualizadaEn">;
type ActualizarTareaDto = Partial<Pick<Tarea, "titulo" | "descripcion" | "completada" | "prioridad" | "etiquetas">>;
type TareaPublica = Readonly<Omit<Tarea, "actualizadaEn">>;
type ResumenTarea = Pick<Tarea, "id" | "titulo" | "completada" | "prioridad">;
// ──────────────────────────────────────────────────────────────
// Servicio genérico de repositorio
// ──────────────────────────────────────────────────────────────
class RepositorioTareas {
private store: Record<string, Tarea> = {};
crear(dto: CrearTareaDto): TareaPublica {
const tarea: Tarea = {
...dto,
id: crypto.randomUUID(),
creadaEn: new Date(),
actualizadaEn: new Date(),
};
this.store[tarea.id] = tarea;
const { actualizadaEn: _descartada, ...publica } = tarea;
return publica as TareaPublica;
}
obtener(id: string): TareaPublica | null {
const tarea = this.store[id];
if (!tarea) return null;
const { actualizadaEn: _descartada, ...publica } = tarea;
return publica as TareaPublica;
}
actualizar(id: string, cambios: ActualizarTareaDto): TareaPublica | null {
const tarea = this.store[id];
if (!tarea) return null;
const actualizada: Tarea = { ...tarea, ...cambios, actualizadaEn: new Date() };
this.store[id] = actualizada;
const { actualizadaEn: _descartada, ...publica } = actualizada;
return publica as TareaPublica;
}
listar(): ResumenTarea[] {
return Object.values(this.store).map(({ id, titulo, completada, prioridad }) => ({
id,
titulo,
completada,
prioridad,
}));
}
eliminar(id: string): boolean {
if (!this.store[id]) return false;
delete this.store[id];
return true;
}
}
// ──────────────────────────────────────────────────────────────
// Prueba rápida
// ──────────────────────────────────────────────────────────────
const repo = new RepositorioTareas();
const nueva = repo.crear({
titulo: "Estudiar utility types",
descripcion: "Completar la lección de TypeScript",
completada: false,
prioridad: "alta",
etiquetas: ["typescript", "aprendizaje"],
});
console.log("Creada:", nueva.titulo);
const actualizada = repo.actualizar(nueva.id, { completada: true });
console.log("¿Completada?", actualizada?.completada); // true
console.log("Resumen:", repo.listar());Cuándo usar cada utility type
| Utility type | Úsalo cuando... |
|---|---|
Partial<T> |
Actualización parcial de entidades (PATCH) |
Required<T> |
Tienes un tipo con opcionales y necesitas garantizar todos |
Readonly<T> |
Parámetros que no deben mutarse (props de componentes) |
Pick<T, K> |
Necesitas un subconjunto de propiedades de una entidad |
Omit<T, K> |
Quieres todo excepto unos pocos campos (ej: id generado) |
Record<K, V> |
Mapas, cachés, configuraciones indexadas |
Extract<T, U> |
Filtrar una unión para quedarte con tipos específicos |
Exclude<T, U> |
Quitar tipos de una unión |
NonNullable<T> |
Asegurar que un tipo no sea null ni undefined |
ReturnType<T> |
Derivar el tipo de retorno sin importarlo explícitamente |
Parameters<T> |
Crear wrappers que reenvían los mismos argumentos |
Awaited<T> |
Obtener el tipo resuelto de una Promise |
En la siguiente lección profundizarás en tipos condicionales y mapped types, que son precisamente los mecanismos internos con los que todos estos utility types están implementados.
Inicia sesión para guardar tu progreso