¿Odias los anuncios? Ir Sin publicidad Hoy

Tipos de utilidad de TypeScript — Los que vale la pena memorizar (y los demás)

Actualizado en

Parcial, Elegir, Omitir, Registrar, TipoDeRetorno, Parámetros, Esperado — ocho tipos útiles con ejemplos antes y después y una opinión honesta sobre cuáles usarás cada semana.

TypeScript Utility Types — The Ones Worth Memorizing (and the Rest) 1
ANUNCIO · ¿ELIMINAR?

Probablemente hayas leído una página de documentación de TypeScript que lista más de 20 tipos útiles en orden alfabético, define cada uno en una oración y te deja preguntándote cuáles de ellos es necesario internalizar y cuáles existen para personas que escriben código interno de marcos. Esta es la versión más honesta: ocho tipos, ejemplos concretos antes y después, y una respuesta directa sobre su uso diario versus su uso ocasional.

Si empiezas desde JSON bruto y aún no has escrito tus interfaces, convertidor de JSON a TypeScript te llevará a una interfaz básica en segundos — luego vuelve aquí para aplicar estos tipos útiles encima.

Partial — Uso semanal. Aprende primero.

Hace que cada propiedad en T sea opcional. El caso canónico es actualizaciones en estilo PATCH, donde solo se envían los campos que cambiaron.

// Before: manually duplicating the interface with optional versions of everything
interface UserUpdate {
  name?: string;
  email?: string;
  age?: number;
}

// After: Partial derives it from the source of truth
interface User {
  name: string;
  email: string;
  age: number;
}

function updateUser(id: string, updates: Partial<User>) {
  // updates.name is string | undefined — TypeScript knows
}

El problema con la versión «antes»: si añades un campo a User, debes recordar añadirlo también a UserUpdate . Con Partial<User>, añadir un campo a la interfaz de origen lo hace disponible automáticamente en las actualizaciones. Los tipos permanecen sincronizados.

Required — Uso mensual, pero te salva en una situación específica.

El inverso de Partial — elimina todas las ? de un tipo. Más útil después de la validación: recibes un objeto de configuración de tipo suelto, validas que todos los campos estén presentes y luego quieres que TypeScript deje de sugerir que todo podría ser indefinido.

// Before: after validating, you still fight undefined checks everywhere
interface Config {
  apiUrl?: string;
  timeout?: number;
  retries?: number;
}

function initApp(config: Config) {
  config.apiUrl?.trim(); // need optional chaining even after checking
}

// After: Required signals "this has been validated, stop warning me"
function validateAndInit(raw: Config): void {
  if (!raw.apiUrl) throw new Error("apiUrl required");
  if (!raw.timeout) throw new Error("timeout required");
  if (!raw.retries) throw new Error("retries required");

  const config = raw as Required<Config>;
  config.apiUrl.trim(); // no optional chaining needed
}

Nota honesta: Required<T> no realiza validación en tiempo de ejecución — aún necesitas escribir las comprobaciones. Solo cambia lo que TypeScript cree en el nivel de tipos. Usa esto para comunicar «este objeto ya ha sido validado» al nivel de tipos.

Pick — Uso semanal. Elige lo que necesitas.

Crea un nuevo tipo que contenga solo las propiedades que nombras de T. Útil cuando necesitas un subconjunto de una interfaz más grande — formas de API pública, modelos de vista, respuestas serializadas.

// Before: manually maintaining a separate interface that drifts from User
interface UserPublicProfile {
  name: string;
  bio: string;
  avatarUrl: string;
}

// After: Pick stays in sync with the source
interface User {
  id: string;
  name: string;
  email: string;       // private — shouldn't go to clients
  passwordHash: string;  // definitely private
  bio: string;
  avatarUrl: string;
}

type UserPublicProfile = Pick<User, "name" | "bio" | "avatarUrl">;

Pick es la herramienta adecuada cuando la lista de propiedades que quieres es más corta que la lista que necesitas exclude. Cuando el caso se invierte, busca Omit.

Omit — Uso semanal. Elige lo que no necesitas.

Complemento de Pick — crea un tipo con todas las propiedades de T excepto las que nombras. Cuando tienes una interfaz grande y solo unos pocos campos para excluir, Omit hace más clara la intención que listar todos los campos que deseas incluir.

// Before: manually listing every field except the sensitive ones
// Easy to miss a new field added to User later
interface UserForClient {
  id: string;
  name: string;
  email: string;
  bio: string;
  avatarUrl: string;
  // passwordHash intentionally omitted — but a future dev might not know that
}

// After: Omit states the exclusion explicitly
type UserForClient = Omit<User, "passwordHash">;
// If User gains a new "address" field, UserForClient gets it automatically

Una cosa que ni Pick ni Omit hacen: cambiar el tipo de propiedades individuales. Si quieres seleccionar campos y y transformar sus tipos, estás buscando un tipo mapeado, que es una herramienta completamente diferente.

Record — Uso semanal. El diccionario tipado.

Crea un tipo de objeto donde todas las claves son del tipo K y todos los valores son del tipo V. La verdadera ventaja surge cuando K es un tipo de unión — TypeScript dará error si faltas una clave. Las firmas de índice generales aceptan objetos incompletos silenciosamente; Record con una clave de unión no lo hace.

// Before: index signature with no exhaustiveness checking
const rolePermissions: { [key: string]: string[] } = {
  admin: ["read", "write", "delete"],
  editor: ["read", "write"],
  // forgot "viewer" — TypeScript doesn't notice
};

// After: Record with a union key enforces completeness
type Role = "admin" | "editor" | "viewer";

const rolePermissions: Record<Role, string[]> = {
  admin: ["read", "write", "delete"],
  editor: ["read", "write"],
  viewer: ["read"],
  // TypeScript error if any Role is missing
  // TypeScript error if you try to add an unknown key
};

La comprobación de exhaustividad es la verdadera ventaja. Importa cuando las claves representan algo significativo, como roles, tipos de eventos, métodos HTTP o códigos de estado — cualquier cosa donde una ausencia de caso sea un error, no simplemente una omisión.

ReturnType — Uso semanal si consumes funciones de terceros.

Extrae el tipo de retorno de una función. Más útil cuando consumes una función de una biblioteca y quieres tipar una variable que contiene su salida — sin necesidad de importar el tipo de retorno por separado (lo que las bibliotecas no siempre exportan de forma limpia).

// Before: manually annotating the return type — it can drift
function getCurrentUser() {
  return { id: "u1", name: "Alice", roles: ["admin"] as const };
}

let cachedUser: { id: string; name: string; roles: readonly string[] };
// If getCurrentUser's return changes, this annotation doesn't update automatically

// After: ReturnType derives it and keeps in sync
type CurrentUser = ReturnType<typeof getCurrentUser>;
let cachedUser: CurrentUser;

// Especially useful with unexported library types:
import { useQuery } from "@tanstack/react-query";
type QueryResult = ReturnType<typeof useQuery>;
// No need to figure out what QueryObserverResult<...> looks like or import it

Parameters — Uso ocasional. Principalmente para envolventes.

Extrae los tipos de parámetros de una función como una tupla. Vista honesta: no llegarás a usar esto semanalmente a menos que escribas muchas funciones envolventes o utilidades de orden superior. Pero cuando lo necesitas, no hay alternativa limpia.

// Before: manually duplicating parameter types in the wrapper
function createEvent(
  type: string,
  payload: Record<string, unknown>,
  timestamp: number
) { /* ... */ }

// Every time createEvent changes, this wrapper needs a manual update:
function loggedCreateEvent(
  type: string,
  payload: Record<string, unknown>,
  timestamp: number
) {
  console.log("Creating event:", type);
  return createEvent(type, payload, timestamp);
}

// After: Parameters keeps the wrapper in sync automatically
function loggedCreateEvent(...args: Parameters<typeof createEvent>) {
  console.log("Creating event:", args[0]);
  return createEvent(...args);
}

El patrón de expansión y resto (...args: Parameters<F>) es el idioma principal. También aparece en mocks de pruebas: jest.fn<ReturnType<F>, Parameters<F>>() te da un mock completamente tipado sin necesidad de importar la firma de la función por separado.

Awaited — Usa cuando necesitas desempaquetar tipos de Promise sin llamar a la función.

Añadido en TypeScript 4.5 (lanzado en noviembre de 2021). Desempaqueta recursivamente Promise<T> para llegar a T. Antes de que existiera, obtener el tipo resuelto de una función asíncrona implicaba escribir un tipo condicional: ReturnType<F> extends Promise<infer U> ? U : never — que falla con Promises anidadas y es difícil de leer en diferencias.

// Before: conditional type to unwrap — breaks on nested Promises
async function fetchUser(id: string) {
  const res = await fetch(`/api/users/${id}`);
  return res.json() as { id: string; name: string; email: string };
}

type FetchedUser =
  ReturnType<typeof fetchUser> extends Promise<infer U> ? U : never;
// Works for Promise<T> but not Promise<Promise<T>>

// After: Awaited handles both cases cleanly
type FetchedUser = Awaited<ReturnType<typeof fetchUser>>;

// Awaited recursively unwraps:
type A = Awaited<Promise<string>>;            // string
type B = Awaited<Promise<Promise<string>>>;  // string
type C = Awaited<string>;                      // string (no-op on non-Promise)

La combinación Awaited<ReturnType<typeof fn>> vale la pena memorizar como unidad — cubre el 90% de la extracción de tipos asíncronos en el código de aplicaciones.

Guía rápida

Tipo útilQué haceFrecuencia de usoCaso principal de uso
Partial<T>Todas las propiedades se vuelven opcionalesSemanalmenteCuerpos de actualización en estilo PATCH
Required<T>Todas las propiedades opcionales se vuelven obligatoriasMensualSeñal de validación post-entrada: «esto está completo»
Pick<T, K>Mantén solo las propiedades nombradasSemanalmenteFormas de API pública, modelos de vista
Omit<T, K>Elimina las propiedades nombradasSemanalmenteEliminación de campos sensibles o irrelevante
Record<K, V>Objeto con claves del tipo K y valores del tipo VSemanalmenteDiccionarios tipados con comprobación de exhaustividad
ReturnType<F>Extrae el tipo de retorno de una funciónSemanal (especialmente con bibliotecas)Tipar una variable a partir de un tipo inferido o no exportado
Parameters<F>Extrae los tipos de parámetros de una función como una tuplaOcasionalFunciones envolventes, mocks tipados
Awaited<T>Desempaqueta recursivamente tipos de PromiseOcasionalObtener el tipo resuelto de funciones asíncronas

Los que no están en esta lista

TypeScript también incluye Readonly<T>, NonNullable<T>, Extract<T,U>, Exclude<T,U>, InstanceType<C>, ConstructorParameters<C>, y varios tipos literales de plantilla. Son reales y ocasionalmente útiles, pero surgen en código de bibliotecas, utilidades generales y programación de niveles de tipos — no en lógica típica de aplicaciones. Si has memorizado los ocho mencionados anteriormente, busca los demás cuando realmente los necesites.

El principio subyacente es el mismo en todos ellos: describir una transformación de un tipo existente en lugar de duplicarlo. Los nombres específicos de los tipos útiles son solo vocabulario para expresar esa intención al nivel de tipos en TypeScript.

¿Quieres eliminar publicidad? Adiós publicidad hoy

Instalar extensiones

Agregue herramientas IO a su navegador favorito para obtener acceso instantáneo y búsquedas más rápidas

añadir Extensión de Chrome añadir Extensión de borde añadir Extensión de Firefox añadir Extensión de Opera

¡El marcador ha llegado!

Marcador es una forma divertida de llevar un registro de tus juegos, todos los datos se almacenan en tu navegador. ¡Próximamente habrá más funciones!

ANUNCIO · ¿ELIMINAR?
ANUNCIO · ¿ELIMINAR?
ANUNCIO · ¿ELIMINAR?

Noticias Aspectos técnicos clave

Involucrarse

Ayúdanos a seguir brindando valiosas herramientas gratuitas

Invítame a un café
ANUNCIO · ¿ELIMINAR?