Tipos de utilidad (Partial, Required, Pick, Omit, Record y más)

Avanzado
TypeScript
TypeScript
Actualizado: 04/05/2026

Diagrama: tutorial-typescript-tipos-utilidad

Partial y Required

Los utility types Partial<T> y Required<T> modifican la obligatoriedad de todas las propiedades de un tipo. Son operaciones inversas entre si.

Partial

Partial<T> convierte todas las propiedades de T en opcionales:

interface Usuario {
    id: number
    nombre: string
    email: string
    edad: number
}

type UsuarioParcial = Partial<Usuario>
// { id?: number; nombre?: string; email?: string; edad?: number }

El caso de uso principal es la actualización parcial de objetos, donde solo se proporcionan los campos que cambian:

function actualizarUsuario(id: number, cambios: Partial<Usuario>): Usuario {
    const usuarioActual: Usuario = {
        id,
        nombre: "Ana",
        email: "ana@ejemplo.com",
        edad: 30
    }
    return { ...usuarioActual, ...cambios }
}

const actualizado = actualizarUsuario(1, { nombre: "Ana Maria" })
console.log(actualizado.nombre)  // "Ana Maria"
console.log(actualizado.email)   // "ana@ejemplo.com" (preservado)

Partial es útil también para opciones de configuración con valores por defecto:

interface OpcionesConexion {
    host: string
    puerto: number
    timeout: number
    reintentos: number
}

const OPCIONES_POR_DEFECTO: OpcionesConexion = {
    host: "localhost",
    puerto: 3000,
    timeout: 5000,
    reintentos: 3
}

function conectar(opciones: Partial<OpcionesConexion> = {}): OpcionesConexion {
    return { ...OPCIONES_POR_DEFECTO, ...opciones }
}

const config = conectar({ puerto: 8080, timeout: 10000 })
console.log(config.host)        // "localhost"
console.log(config.puerto)      // 8080
console.log(config.reintentos)  // 3

Required

Required<T> hace todas las propiedades obligatorias, eliminando los modificadores ?:

interface Formulario {
    nombre?: string
    email?: string
    telefono?: string
}

type FormularioCompleto = Required<Formulario>
// { nombre: string; email: string; telefono: string }

function enviarFormulario(datos: Required<Formulario>): void {
    console.log(`Enviando: ${datos.nombre}, ${datos.email}, ${datos.telefono}`)
}

// Error: falta la propiedad "telefono"
// enviarFormulario({ nombre: "Luis", email: "luis@ejemplo.com" })

enviarFormulario({ nombre: "Luis", email: "luis@ejemplo.com", telefono: "600123456" })

Required es especialmente útil cuando un tipo base tiene propiedades opcionales para la creación, pero en cierto punto del flujo todas deben estar presentes.

Readonly

Readonly<T> convierte todas las propiedades en solo lectura, impidiendo su reasignación:

interface Estado {
    contador: number
    activo: boolean
    items: string[]
}

const estado: Readonly<Estado> = {
    contador: 0,
    activo: true,
    items: ["a", "b"]
}

// Error: no se puede asignar a propiedad readonly
// estado.contador = 1
// estado.activo = false

Readonly solo protege el primer nivel de propiedades. Los objetos y arrays anidados siguen siendo mutables internamente. Para inmutabilidad profunda es necesario crear un tipo DeepReadonly personalizado.

function congelar<T extends object>(obj: T): Readonly<T> {
    return Object.freeze(obj)
}

const config = congelar({ api: "https://api.ejemplo.com", version: 2 })
console.log(config.api)  // "https://api.ejemplo.com"
// config.api = "otra"  // Error en tiempo de compilación

Pick y Omit

Pick<T, K> construye un tipo seleccionando solo las propiedades indicadas. Omit<T, K> hace lo contrario, excluyendo las propiedades indicadas.

Pick

interface Producto {
    id: number
    nombre: string
    descripcion: string
    precio: number
    stock: number
    categoria: string
}

type ProductoResumen = Pick<Producto, "id" | "nombre" | "precio">
// { id: number; nombre: string; precio: number }

type ProductoInventario = Pick<Producto, "id" | "nombre" | "stock">
// { id: number; nombre: string; stock: number }

function mostrarResumen(producto: ProductoResumen): void {
    console.log(`${producto.nombre}: ${producto.precio} EUR`)
}

mostrarResumen({ id: 1, nombre: "Monitor", precio: 300 })

Omit

type ProductoSinId = Omit<Producto, "id">
// { nombre: string; descripcion: string; precio: number; stock: number; categoria: string }

type CrearProducto = Omit<Producto, "id" | "stock">
// { nombre: string; descripcion: string; precio: number; categoria: string }

function crearProducto(datos: CrearProducto): Producto {
    return {
        ...datos,
        id: Math.floor(Math.random() * 10000),
        stock: 0
    }
}

const nuevo = crearProducto({
    nombre: "Teclado",
    descripcion: "Teclado mecánico",
    precio: 75,
    categoria: "Perifericos"
})
console.log(nuevo.id)     // número aleatorio
console.log(nuevo.stock)  // 0

Combinación de Pick y Omit

interface Empleado {
    id: number
    nombre: string
    email: string
    departamento: string
    salario: number
    fechaAlta: Date
}

// Solo datos publicos
type EmpleadoPublico = Omit<Empleado, "salario" | "fechaAlta">

// Solo datos de contacto
type Contacto = Pick<Empleado, "nombre" | "email">

// Para actualización: sin id ni fechaAlta, y parcial
type ActualizarEmpleado = Partial<Omit<Empleado, "id" | "fechaAlta">>

function actualizar(id: number, cambios: ActualizarEmpleado): void {
    console.log(`Actualizando empleado ${id}:`, cambios)
}

actualizar(1, { nombre: "Ana Garcia", departamento: "IT" })

Record

Record<K, V> construye un tipo de objeto cuyas claves son de tipo K y cuyos valores son de tipo V:

type Rol = "admin" | "editor" | "lector"

type Permisos = Record<Rol, boolean>
// { admin: boolean; editor: boolean; lector: boolean }

const permisos: Permisos = {
    admin: true,
    editor: true,
    lector: false
}

Record es ideal para crear mapas tipados y diccionarios:

type CodigoHTTP = 200 | 201 | 400 | 404 | 500

const mensajesHTTP: Record<CodigoHTTP, string> = {
    200: "OK",
    201: "Creado",
    400: "Peticion incorrecta",
    404: "No encontrado",
    500: "Error interno del servidor"
}

console.log(mensajesHTTP[404])  // "No encontrado"
interface ConfiguracionModulo {
    activo: boolean
    prioridad: number
    opciones: Record<string, unknown>
}

type Modulos = "auth" | "cache" | "logger"

const configuracion: Record<Modulos, ConfiguracionModulo> = {
    auth: { activo: true, prioridad: 1, opciones: { secreto: "abc" } },
    cache: { activo: true, prioridad: 2, opciones: { ttl: 3600 } },
    logger: { activo: false, prioridad: 3, opciones: { nivel: "info" } }
}

Exclude y Extract

Exclude<T, U> elimina de una unión los miembros asignables a U. Extract<T, U> hace lo contrario, conservando solo los miembros asignables a U.

type Evento = "click" | "scroll" | "keydown" | "keyup" | "focus" | "blur"

type EventoTeclado = Extract<Evento, "keydown" | "keyup">
// "keydown" | "keyup"

type EventoSinTeclado = Exclude<Evento, "keydown" | "keyup">
// "click" | "scroll" | "focus" | "blur"

Estos tipos operan sobre uniones de cualquier tipo, no solo strings:

type Dato = string | number | boolean | null | undefined

type DatoDefinido = Exclude<Dato, null | undefined>
// string | number | boolean

type DatoPrimitivo = Extract<Dato, string | number | boolean>
// string | number | boolean
type Forma =
    | { tipo: "circulo"; radio: number }
    | { tipo: "rectangulo"; ancho: number; alto: number }
    | { tipo: "triangulo"; base: number; altura: number }

type FormaConArea = Extract<Forma, { tipo: "circulo" } | { tipo: "rectangulo" }>
// { tipo: "circulo"; radio: number } | { tipo: "rectangulo"; ancho: number; alto: number }

NonNullable

NonNullable<T> elimina null y undefined de una unión de tipos:

type MaybeString = string | null | undefined

type DefiniteString = NonNullable<MaybeString>
// string
interface Respuesta {
    datos: string | null
    error: string | undefined
}

function procesar(datos: NonNullable<Respuesta["datos"]>): void {
    // datos es string, nunca null
    console.log(datos.toUpperCase())
}

function validarRespuesta(resp: Respuesta): void {
    if (resp.datos != null) {
        procesar(resp.datos)  // TypeScript sabe que no es null aquí
    }
}

Parameters y ReturnType

Parameters<T> extrae los tipos de los parámetros de una función como una tupla. ReturnType<T> extrae el tipo de retorno.

function crearUsuario(nombre: string, edad: number, activo: boolean): {
    id: number
    nombre: string
    edad: number
    activo: boolean
} {
    return { id: Date.now(), nombre, edad, activo }
}

type ParamsCrear = Parameters<typeof crearUsuario>
// [string, number, boolean]

type ResultadoCrear = ReturnType<typeof crearUsuario>
// { id: number; nombre: string; edad: number; activo: boolean }

Estos tipos son especialmente útiles para crear wrappers y decoradores:

function conLog<T extends (...args: any[]) => any>(
    fn: T,
    nombre: string
): (...args: Parameters<T>) => ReturnType<T> {
    return (...args: Parameters<T>): ReturnType<T> => {
        console.log(`Llamando a ${nombre} con:`, args)
        const resultado = fn(...args)
        console.log(`${nombre} devolvio:`, resultado)
        return resultado
    }
}

function sumar(a: number, b: number): number {
    return a + b
}

const sumarConLog = conLog(sumar, "sumar")
sumarConLog(3, 5)
// Llamando a sumar con: [3, 5]
// sumar devolvio: 8
type Manejador = (req: { url: string; metodo: string }, res: { send: (d: string) => void }) => void

type ParamsManejador = Parameters<Manejador>
// [{ url: string; metodo: string }, { send: (d: string) => void }]

type RetornoManejador = ReturnType<Manejador>
// void

InstanceType

InstanceType<T> extrae el tipo de instancia de un constructor:

class Conexion {
    constructor(public host: string, public puerto: number) {}
    conectar(): void {
        console.log(`Conectando a ${this.host}:${this.puerto}`)
    }
}

type TipoConexion = InstanceType<typeof Conexion>
// Conexion

function crearInstancia<T extends new (...args: any[]) => any>(
    Constructor: T,
    ...args: ConstructorParameters<T>
): InstanceType<T> {
    return new Constructor(...args)
}

const conn = crearInstancia(Conexion, "localhost", 5432)
conn.conectar()  // "Conectando a localhost:5432"

InstanceType es útil en patrones donde se pasan clases como parámetros y se necesita tipar las instancias resultantes:

class Logger {
    log(mensaje: string): void { console.log(mensaje) }
}

class Cache {
    datos = new Map<string, unknown>()
    obtener(clave: string): unknown { return this.datos.get(clave) }
}

type Servicio = typeof Logger | typeof Cache

function inicializar<T extends new () => any>(
    servicios: T[]
): InstanceType<T>[] {
    return servicios.map(S => new S())
}

const [logger, cache] = inicializar([Logger, Cache])

Awaited

Awaited<T> desempaqueta el tipo de una Promise, incluso promesas anidadas. Es el tipo que resulta de usar await:

type A = Awaited<Promise<string>>
// string

type B = Awaited<Promise<Promise<number>>>
// number

type C = Awaited<string>
// string (no es Promise, devuelve T directamente)

Awaited es útil para tipar resultados de funciones asíncronas:

async function obtenerDatos(): Promise<{ id: number; nombre: string }[]> {
    const res = await fetch("/api/datos")
    return res.json()
}

type Datos = Awaited<ReturnType<typeof obtenerDatos>>
// { id: number; nombre: string }[]
function procesarPromesas<T extends Promise<any>[]>(
    ...promesas: T
): Promise<{ [K in keyof T]: Awaited<T[K]> }> {
    return Promise.all(promesas) as any
}

async function ejemplo() {
    const [usuario, config] = await procesarPromesas(
        Promise.resolve({ nombre: "Ana" }),
        Promise.resolve({ tema: "oscuro" })
    )
    console.log(usuario.nombre)  // "Ana"
    console.log(config.tema)     // "oscuro"
}

Combinaciones prácticas de utility types

Los utility types alcanzan su máximo potencial cuando se combinan para modelar transformaciones complejas:

interface Articulo {
    id: number
    titulo: string
    contenido: string
    autor: string
    etiquetas: string[]
    publicado: boolean
    fechaCreacion: Date
}

// Para crear: sin id ni fecha, contenido y etiquetas opcionales
type CrearArticulo = Omit<Articulo, "id" | "fechaCreacion"> &
    Partial<Pick<Articulo, "contenido" | "etiquetas">>

// Para listar: solo datos básicos
type ArticuloLista = Pick<Articulo, "id" | "titulo" | "autor" | "publicado">

// Para editar: todo parcial excepto id
type EditarArticulo = Partial<Omit<Articulo, "id">> & Pick<Articulo, "id">

function crear(datos: CrearArticulo): Articulo {
    return {
        ...datos,
        id: Date.now(),
        contenido: datos.contenido ?? "",
        etiquetas: datos.etiquetas ?? [],
        fechaCreacion: new Date()
    }
}

const articulo = crear({
    titulo: "Utility types en TypeScript",
    autor: "Elena",
    publicado: false
})
console.log(articulo.id)              // timestamp
console.log(articulo.etiquetas)       // []
console.log(articulo.fechaCreacion)   // Date actual
// Tipo que hace readonly solo ciertas propiedades
type ReadonlyPick<T, K extends keyof T> =
    Readonly<Pick<T, K>> & Omit<T, K>

interface Documento {
    id: number
    titulo: string
    contenido: string
    version: number
}

type DocConIdFijo = ReadonlyPick<Documento, "id" | "version">

const doc: DocConIdFijo = { id: 1, titulo: "Doc", contenido: "...", version: 1 }
doc.titulo = "Nuevo titulo"  // OK
// doc.id = 2               // Error: readonly
// doc.version = 2          // Error: readonly

Fuentes y referencias

Documentación oficial y recursos externos para profundizar en TypeScript

Documentación oficial de TypeScript
Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, TypeScript es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de TypeScript

Explora más contenido relacionado con TypeScript y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Aplicar Partial, Required y Readonly para modificar propiedades de tipos. Usar Pick, Omit y Record para crear tipos derivados. Extraer tipos con Exclude, Extract, NonNullable, Parameters, ReturnType, InstanceType y Awaited.