Tipos utilitarios de TypeScript con Claude Code: guía práctica
Aprende Pick, Omit, Partial, Record, ReturnType y Awaited con ejemplos ejecutables para Claude Code.
Los tipos utilitarios de TypeScript sirven para convertir un tipo existente en otro tipo con un propósito más concreto.
Si copias a mano un tipo User para crear versiones de vista pública, formulario y actualización API, esas copias se desincronizan cuando cambia un campo.
Claude Code puede ayudarte a refactorizarlo, pero tú debes entender la intención para revisar si la salida es segura.
Esta guía explica Pick, Omit, Partial, Required, Readonly, Record, ReturnType y Awaited con lenguaje sencillo.
Después muestra cómo pedirle a Claude Code una implementación práctica mediante ejemplos que puedes copiar y ejecutar.
Usa TypeScript Handbook Utility Types como referencia oficial, y activa TSConfig strict cuando quieras detectar errores temprano.
Para ampliar dentro de ClaudeCodeLab, revisa consejos prácticos de TypeScript y la guía de genéricos de TypeScript.
Modelo mental sencillo
Un tipo utilitario es una forma segura de “copiar y remodelar” tipos. Se parece a duplicar una hoja de cálculo, quitar columnas, marcar algunas como opcionales y usar esa versión en otro flujo. La diferencia es que TypeScript revisa esa transformación antes de ejecutar el código.
Pick<User, "id" | "name"> toma solo id y name de User.
Omit<User, "passwordHash"> conserva casi todo, pero excluye passwordHash.
Usa Pick cuando el tipo destino es pequeño y explícito.
Usa Omit cuando el tipo destino se parece mucho al original y solo necesitas eliminar campos peligrosos o irrelevantes.
Partial<User> hace opcionales todas las propiedades.
Es útil para borradores, PATCH y formularios en progreso, pero puede volver opcional un email que debería ser obligatorio al crear una cuenta.
Required<User> hace lo contrario: convierte propiedades opcionales en obligatorias.
Readonly<User> evita reasignaciones y documenta que una configuración o tabla maestra no debería mutar.
Record<Keys, Type> crea un diccionario con claves conocidas.
ReturnType<typeof fn> extrae el tipo que devuelve una función.
Awaited<Promise<T>> extrae el valor que obtienes después de await.
La combinación Awaited<ReturnType<typeof fetchSomething>> mantiene sincronizados los tipos de API y UI.
flowchart LR
A["Source type: User"] --> B["Pick: public view"]
A --> C["Omit: remove secrets"]
A --> D["Partial: update input"]
A --> E["Required: validated input"]
A --> F["Readonly: fixed settings"]
G["Function"] --> H["ReturnType"]
I["Promise"] --> J["Awaited"]
Comparación rápida
| Tipo | Qué hace | Uso práctico | Cuidado con |
|---|---|---|---|
Pick<T, K> | Selecciona claves concretas | Listas, perfiles públicos, tarjetas | Lo no seleccionado no existe en ese tipo |
Omit<T, K> | Excluye claves concretas | Inputs de creación, salida pública, logs | No borra valores en runtime |
Partial<T> | Hace todas las claves opcionales | Borradores, PATCH, formularios parciales | Es superficial |
Required<T> | Hace todas las claves obligatorias | Datos validados antes de guardar | Puede exigir demasiado pronto |
Readonly<T> | Impide reasignar propiedades | Configuración, permisos, constantes | Los objetos anidados requieren más cuidado |
Record<K, T> | Crea un diccionario de claves fijas | Permisos por rol, etiquetas, precios | Record<string, T> suele ser demasiado amplio |
ReturnType<T> | Extrae el retorno de una función | Sincronizar API y UI | Usa typeof functionName |
Awaited<T> | Extrae el valor resuelto de una Promise | Resultados de funciones async | No hace await en runtime |
Pega esta tabla en Claude Code y pídele que revise si cada tipo utilitario coincide con el uso real.
En proyectos con strict: true, los tipos vagos fallan antes, y eso reduce el coste de corrección.
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"skipLibCheck": true
}
}
Caso 1: derivar tipos de UI y formulario desde User
Las pantallas de administración suelen necesitar varias versiones de la misma entidad. El tipo de base de datos, el tipo público y el tipo de formulario no deberían mantenerse como copias manuales. Mantén un tipo fuente y deriva el resto con utilitarios.
type UserRole = "admin" | "editor" | "viewer";
interface User {
id: string;
name: string;
email: string;
role: UserRole;
bio: string;
passwordHash: string;
createdAt: Date;
updatedAt: Date;
}
type PublicUser = Pick<User, "id" | "name" | "role" | "bio">;
type UserDraft = Partial<Omit<User, "id" | "passwordHash" | "createdAt" | "updatedAt">>;
type CreateUserInput =
Required<Pick<User, "name" | "email" | "role">> &
Partial<Pick<User, "bio">>;
function buildCreatePayload(input: CreateUserInput): Omit<User, "id" | "createdAt" | "updatedAt"> {
return {
name: input.name,
email: input.email,
role: input.role,
bio: input.bio ?? "",
passwordHash: "hashed-by-server",
};
}
const publicUser: PublicUser = {
id: "u_001",
name: "Masa",
role: "admin",
bio: "Claude Code workflow designer",
};
const draft: UserDraft = {
name: "Draft user",
bio: "Saved before email is confirmed",
};
console.log(publicUser);
console.log(buildCreatePayload({ name: "Aki", email: "aki@example.com", role: "editor" }));
console.log(draft);
La instrucción a Claude Code debe separar qué se conserva, qué se elimina y cuándo algo es obligatorio.
Create public display, form draft, and create-API types from the User type.
Do not expose passwordHash.
For creation, require only name, email, and role. Keep bio optional.
Use Pick/Omit/Partial/Required and briefly explain why each utility type is used.
En producción, acompaña estos tipos con validación en runtime, por ejemplo con Zod. Los tipos utilitarios revisan la forma antes de ejecutar; no demuestran que los datos enviados por un usuario sean confiables.
Caso 2: fijar funciones por plan con Record
Planes de precio, roles y tablas de estado encajan muy bien con Record.
El compilador puede avisarte si falta el plan team o si escribiste mal prioritySupport.
type Plan = "free" | "pro" | "team";
type Feature = "exportPdf" | "inviteMember" | "prioritySupport";
const featureMatrix: Readonly<Record<Plan, Readonly<Record<Feature, boolean>>>> = {
free: {
exportPdf: false,
inviteMember: false,
prioritySupport: false,
},
pro: {
exportPdf: true,
inviteMember: false,
prioritySupport: false,
},
team: {
exportPdf: true,
inviteMember: true,
prioritySupport: true,
},
};
function canUse(plan: Plan, feature: Feature): boolean {
return featureMatrix[plan][feature];
}
console.log(canUse("pro", "exportPdf"));
console.log(canUse("free", "prioritySupport"));
Readonly deja claro que la matriz no debe modificarse durante la ejecución.
Como es superficial, el ejemplo también marca el Record interno como readonly.
Para estructuras más profundas, considera as const o un helper de deep readonly propio del proyecto.
Caso 3: reutilizar resultados API con ReturnType y Awaited
Si escribes por separado los tipos del cliente API y de la UI, cada cambio de respuesta cuesta más.
ReturnType y Awaited permiten derivar el tipo desde la función async real.
async function fetchInvoice(invoiceId: string) {
return {
id: invoiceId,
status: "paid" as const,
amount: 48000,
currency: "JPY" as const,
paidAt: new Date("2026-06-02T10:00:00+09:00"),
};
}
type Invoice = Awaited<ReturnType<typeof fetchInvoice>>;
type InvoiceSummary = Pick<Invoice, "id" | "status" | "amount" | "currency">;
function formatInvoice(invoice: InvoiceSummary): string {
return `${invoice.id}: ${invoice.amount.toLocaleString()} ${invoice.currency} (${invoice.status})`;
}
async function main() {
const invoice = await fetchInvoice("inv_20260602");
console.log(formatInvoice(invoice));
}
main();
Cuando la función API es el límite confiable, pide a Claude Code que no escriba otro tipo de respuesta a mano. Si el límite es una API externa o input de usuario, añade validación en runtime y manejo de errores.
Caso 4: usar Partial en el nivel correcto
Partial<T> es superficial.
No convierte automáticamente en opcionales los campos dentro de objetos anidados, y ese detalle causa muchos bugs pequeños.
interface Profile {
id: string;
displayName: string;
settings: {
emailNotification: boolean;
smsNotification: boolean;
};
}
type ProfilePatch =
Omit<Partial<Profile>, "settings"> & {
settings?: Partial<Profile["settings"]>;
};
function patchProfile(current: Profile, patch: ProfilePatch): Profile {
return {
...current,
...patch,
settings: {
...current.settings,
...patch.settings,
},
};
}
const profile: Profile = {
id: "p_001",
displayName: "Masa",
settings: {
emailNotification: true,
smsNotification: false,
},
};
console.log(patchProfile(profile, { settings: { smsNotification: true } }));
Con solo ProfilePatch = Partial<Profile>, actualizar settings seguiría exigiendo el objeto completo.
En equipos con principiantes, un tipo concreto como este suele ser más mantenible que un genérico deep partial demasiado ingenioso.
Fallos concretos que debes evitar
Omit elimina una clave del tipo, no del objeto en runtime.
Si devuelves logs o respuestas públicas, debes retirar realmente el valor sensible.
interface Account {
id: string;
email: string;
passwordHash: string;
}
type SafeAccount = Omit<Account, "passwordHash">;
function toSafeAccount(account: Account): SafeAccount {
const { passwordHash, ...safeAccount } = account;
return safeAccount;
}
console.log(toSafeAccount({
id: "a_001",
email: "masa@example.com",
passwordHash: "secret",
}));
Record<string, T> suele ser demasiado amplio para reglas de negocio.
Si conoces las claves permitidas, usa una unión como type Plan = "free" | "pro" | "team".
Required<T> aplicado demasiado pronto puede hacer que un formulario pida más de lo necesario.
Úsalo cuando ya tengas datos validados antes de guardar.
Awaited<T> solo describe el tipo resuelto de una Promise.
No espera en runtime; todavía necesitas await o .then().
Si confundes eso, Claude Code puede ordenar los tipos y olvidar estados de carga o error.
Prompt de revisión para Claude Code
Después de implementar, pide una revisión enfocada en riesgos.
Review this TypeScript type design.
1. Are Pick/Omit/Partial/Required/Readonly/Record matched to their use cases?
2. Are secrets removed at runtime, not only with Omit?
3. Does Partial make create inputs too loose?
4. Do ReturnType and Awaited reduce duplicated API types?
5. Are any vague any or broad string types left under strict settings?
Este encuadre evita que la revisión se quede en “tipos bonitos”.
Masa se encontró con este problema en una pantalla administrativa pequeña: usar Partial en todas partes hacía que un email vacío pareciera aceptable hasta el guardado.
Separar “borrador”, “creación” y “guardado” en tipos derivados hizo que los parches de Claude Code fueran mucho más fáciles de revisar.
Conclusión
Los tipos utilitarios de TypeScript no son acrobacia de tipos.
Son herramientas prácticas para expresar diferencias entre datos públicos, borradores, inputs validados, configuración fija y resultados async sin duplicar definiciones.
Usa Pick y Omit para controlar campos, Partial y Required para modelar etapas, Readonly y Record para proteger configuración, y ReturnType más Awaited para reducir tipos API duplicados.
ClaudeCodeLab ayuda a equipos a aplicar Claude Code en arquitectura TypeScript, CMS de contenido, herramientas internas y funnels monetizados. Si tu proyecto tiene tipos pero aún aparecen errores evitables, solicita formación y consultoría de Claude Code.
Probé los ejemplos de este artículo con mentalidad de TypeScript estricto.
La mejora más clara aparece con ReturnType y Awaited: cuando cambia una respuesta API, la UI muestra antes dónde hay que ajustar.
La advertencia importante es Omit: nunca elimina secretos en runtime, así que las funciones de respuesta pública deben retirar propiedades de forma explícita.
PDF gratis: cheatsheet de Claude Code
Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.
Cuidamos tus datos y no enviamos spam.
Sobre el autor
Masa
Ingeniero enfocado en workflows prácticos con Claude Code.
Artículos relacionados
Escalera de permisos de Claude Code para ampliar acceso sin perder control
Pasa de read-only a ediciones limitadas, comandos de prueba y checks de deploy con menos riesgo.
Claude Code Small PR Proof Pack: cambios pequeños que sí se pueden revisar
Un paquete de prueba para PRs de Claude Code: diff, checks, URL pública, CTA y rollback.
Gate de revisión antes del commit con Claude Code
Cómo revisar con Claude Code antes del commit: diff, build, URL pública, Gumroad, consultoría, tests y archivos ajenos.