Utility Types de TypeScript que deberías conocer

En el día a día de un proyecto TypeScript nos encontramos con objetos que cambian de forma según el contexto: al crear, actualizar o leer datos, o al exponer una API. Los utility types son herramientas del lenguaje que nos permiten transformar tipos de forma declarativa, evitando código repetitivo y errores comunes.
Partial
Hace que todas las propiedades de un tipo sean opcionales. Es ideal para representar actualizaciones parciales de recursos.
type User = { id: number; name: string; email: string; };
type UserUpdate = Partial<User>;
// UserUpdate equivale a { id?: number; name?: string; email?: string; }
Pick y Omit
Pick crea un nuevo tipo seleccionando sólo algunas propiedades, mientras que Omit hace lo contrario, excluyendo las que no queremos.
type User = { id: number; name: string; email: string; role: string; };
// Sólo id y name
type UserIdName = Pick<User, 'id' | 'name'>;
// Todo menos el email
type UserWithoutEmail = Omit<User, 'email'>;
Record
Construye un tipo cuyas claves son de un conjunto específico y cuyos valores comparten el mismo tipo. Muy útil para mapear enums a valores.
enum Color { Red = 'red', Green = 'green', Blue = 'blue' }
type ColorHex = Record<Color, string>;
// Resulta en { red: string; green: string; blue: string; }
Required y Readonly
Required vuelve obligatorias todas las propiedades de un tipo, mientras que Readonly las convierte en de solo lectura.
type PartialUser = { id?: number; name?: string; };
type CompleteUser = Required<PartialUser>; // { id: number; name: string; }
type ImmutableUser = Readonly<CompleteUser>; // todas las propiedades son readonly
ReturnType y Parameters
Permiten extraer, respectivamente, el tipo de retorno y los tipos de los parámetros de una función. Son la base de muchas abstracciones avanzadas.
function fetchUser(id: number): Promise<User> { / ... / }
type FetchUserReturn = ReturnType<typeof fetchUser>; // Promise
type FetchUserParams = Parameters<typeof fetchUser>; // [number]
Exclude, Extract y NonNullable
Estos tres tipos operan sobre uniones. Exclude elimina miembros, Extract conserva sólo los que coinciden, y NonNullable elimina null y undefined.
type All = string | number | null | undefined;
type NoNull = NonNullable<All>; // string | number
type Numbers = Extract<All, number>; // number
type NoString = Exclude<All, string>; // number | null | undefined
Uppercase y Lowercase
Transforman literales de cadena a su versión en mayúsculas o minúsculas, lo que facilita la normalización de claves.
type Foo = 'hello' | 'world';
type UpperFoo = Uppercase<Foo>; // 'HELLO' | 'WORLD'
type LowerFoo = Lowercase<UpperFoo>; // 'hello' | 'world'
Awaited
Desenvuelve el tipo que resulta de await sobre una promesa. Muy práctico cuando trabajamos con funciones asíncronas genéricas.
async function getData(): Promise<{ data: string }> { / ... / }
type Resolved = Awaited<ReturnType<typeof getData>>; // { data: string }
Estos utility types son sólo la punta del iceberg; TypeScript incluye muchos más y la comunidad sigue creando variantes útiles. Aprender a combinarlos te permite escribir APIs tipadas de forma mucho más expresiva, reducir la duplicación de tipos y, sobre todo, evitar errores que en tiempo de ejecución serían difíciles de detectar.
Mi consejo práctico: cuando necesites crear un nuevo tipo a partir de otro, primero revisa si alguno de los utility types estándar ya cubre tu caso. Ahorrarás tiempo y mantendrás tu código más legible para todo el equipo.