Programming Language

Tipos Avanzados de TypeScript

Los utility types, conditional types y mapped types te permiten transformar y componer tipos con precisión. Así es como funcionan y cuándo utilizarlos.

intermediate16 min readUpdated 15 sept 2026
typescript
type Users = {
  ada: { age: 36 };
  bob: { age: 28 };
};

type UserNames = keyof Users;
type Ages = Users[UserNames];
Utility types
Transformaciones de tipos integradas (Partial, Pick, Record)
Conditional types
T extends U ? X : Y
Mapped types
Transforman propiedades de un tipo existente
Template literals
Manipulación de strings a nivel de tipo
Palabra clave infer
Extrae tipos desde el interior de otros tipos
Condicionales distributivos
Aplican condiciones a cada miembro de una unión

Por que importa

Por qué importan los tipos avanzados

Transformación de tipos

Construye nuevos tipos a partir de los existentes sin duplicar definiciones. Cambia, filtra y remodela tipos para que se ajusten a tus necesidades.

Contratos precisos

Modela datos complejos con tipos exactos que detectan errores que otros pasan por alto. Uniones discriminadas, tipos exactos y branded types.

Menos repetición

Deriva tipos automáticamente en lugar de mantener definiciones paralelas. Una única fuente de verdad, muchos tipos derivados.

La imagen completa

Los tres pilares de la transformación de tipos

Los utility types, conditional types y mapped types son las herramientas que te permiten construir cualquier tipo a partir de otros existentes sin repetirte.

Utility types

Transformaciones integradas

Partial, Required, Pick, Omit, Record, Extract, Exclude y otros tipos estándar que modifican tipos existentes.

Conditional types

Lógica a nivel de tipo

If/else a nivel de tipo. T extends U ? X : Y te permite ramificar tipos basados en condiciones.

Mapped types

Transformación de propiedades

Itera sobre las claves de un tipo y transforma cada propiedad. Es la base de la mayoría de los utility types.

Tipos avanzados de un vistazo

Qué puedes construir

Partial<T>

Hace que todas las propiedades sean opcionales.

Required<T>

Hace que todas las propiedades sean obligatorias.

Pick<T, K>

Extrae un subconjunto de propiedades.

Omit<T, K>

Elimina propiedades específicas.

Record<K, V>

Crea un tipo de objeto con claves K y valores V.

Extract<U>

Extrae los miembros de una unión que coinciden con un tipo.

Una breve historia

De alias simples a la programación a nivel de tipos

  1. 2016

    Llegan los utility types

    TypeScript 2.1 introduce los mapped types y utilidades integradas como Partial, Readonly y Pick.

    16
  2. 2018

    Llegan los conditional types

    TypeScript 2.8 añade conditional types, habilitando la lógica a nivel de tipo y la palabra clave infer.

    18
  3. 2019

    Tipos recursivos

    TypeScript 4.1 habilita los conditional types recursivos y los template literal types.

    19
  4. 2021

    Template literal types

    La manipulación de strings a nivel de tipo abre nuevas posibilidades para el diseño de APIs.

    21
  5. 2023

    Operador satisfies

    TypeScript 4.9 añade satisfies para validar valores sin perder los tipos literales.

    23
  6. Today

    Programación a nivel de tipo

    Los tipos avanzados son esenciales para librerías, clientes de API y estados de aplicación complejos.

    Today

La guia completa

Tipos Avanzados de TypeScript: Todo lo que necesitas saber

¿Qué son los tipos avanzados de TypeScript?

Los tipos avanzados son las herramientas que te permiten transformar, componer y manipular tipos a nivel de tipo. En lugar de definir manualmente tipos paralelos para cada variación, los derivas automáticamente de los ya existentes.

Si los conceptos básicos te proporcionan los tipos y los genéricos te permiten escribir código reutilizable, los tipos avanzados te permiten construir un sistema de tipos que describe tus datos con precisión, detectando errores que los enfoques más simples pasarían por alto.

Los tres pilares son los utility types (transformaciones integradas), los conditional types (lógica a nivel de tipo) y los mapped types (transformación de propiedades). Juntos, forman un pequeño lenguaje para trabajar con tipos.

Utility types

TypeScript incluye tipos integrados que permiten modificar otros tipos:

Partial y Required

interface User {
  id: number;
  name: string;
  email: string;
}

// All properties optional
type UserUpdate = Partial<User>;
// { id?: number; name?: string; email?: string; }

// All properties required
type StrictUser = Required<Partial<User>>;
// { id: number; name: string; email: string; }

Pick y Omit

// Extract specific properties
type UserPreview = Pick<User, "id" | "name">;
// { id: number; name: string; }

// Remove specific properties
type UserWithoutEmail = Omit<User, "email">;
// { id: number; name: string; }

Record

// Create object type with specific keys and values
type Scores = Record<string, number>;
// { [key: string]: number }

const scores: Scores = { math: 95, science: 87 };

// With union keys
type StatusMap = Record<"idle" | "loading" | "done", string>;

Extract y Exclude

type Status = "idle" | "loading" | "success" | "error";

// Keep only matching members
type ActiveStatus = Extract<Status, "loading" | "success">;
// "loading" | "success"

// Remove matching members
type InactiveStatus = Exclude<Status, "loading" | "success">;
// "idle" | "error"

ReturnType y Parameters

function createUser(name: string, age: number) {
  return { name, age, id: Date.now() };
}

type User = ReturnType<typeof createUser>;
// { name: string; age: number; id: number }

type Args = Parameters<typeof createUser>;
// [string, number]

NonNullable

type Maybe = string | null | undefined;
type Definite = NonNullable<Maybe>;
// string

Mapped types

Los mapped types iteran sobre las claves de un tipo existente y transforman cada propiedad:

type ReadOnly<T> = {
  readonly [K in keyof T]: T[K];
};

type MutableUser = ReadOnly<User>;
// All properties are readonly

Agregar modificadores

// Add optional
type Optional<T> = {
  [K in keyof T]?: T[K];
};

// Remove optional
type Concrete<T> = {
  [K in keyof T]-?: T[K];
};

// Remove readonly
type Mutable<T> = {
  -readonly [K in keyof T]: T[K];
};

Los operadores - y + eliminan o agregan modificadores durante el mapeo.

Remapeo de claves con as

TypeScript 4.1+ permite remapear las claves durante el mapeo:

type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};

type UserGetters = Getters<User>;
// { getId: () => number; getName: () => string; getEmail: () => string; }

Tipos condicionales

Los tipos condicionales permiten expresar lógica a nivel de tipos:

type IsString<T> = T extends string ? true : false;

type A = IsString<"hello">; // true
type B = IsString<42>;      // false

La palabra clave infer

infer extrae un tipo desde el interior de otro tipo:

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

type Fn = () => string;
type Result = ReturnType<Fn>; // string

Ejemplos prácticos

// Extract array element type
type ElementOf<T> = T extends (infer E)[] ? E : never;
type Num = ElementOf<number[]>; // number

// Unwrap promises
type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;
type Deep = Awaited<Promise<Promise<string>>>; // string

// Extract object property types
type ValuesOf<T> = T[keyof T];
type UserValues = ValuesOf<User>; // number | string

Tipos condicionales distributivos

Cuando un tipo condicional se distribuye sobre una unión, cada miembro se evalúa por separado:

type ToArray<T> = T extends any ? T[] : never;

type Result = ToArray<string | number>;
// string[] | number[] — not (string | number)[]

Esto es útil para tipos como Extract y Exclude que filtran miembros de una unión.

Tipos de template literal

Los tipos de template literal permiten manipular cadenas de texto a nivel de tipos:

type EventName<T extends string> = `on${Capitalize<T>}`;

type ClickEvent = EventName<"click">; // "onClick"
type FocusEvent = EventName<"focus">; // "onFocus"

Con mapped types

type EventHandlers<T extends string> = {
  [K in T as `on${Capitalize<K>}`]: () => void;
};

type MouseEvents = EventHandlers<"click" | "hover" | "focus">;
// { onClick: () => void; onHover: () => void; onFocus: () => void; }

Coincidencia de patrones de cadenas (String pattern matching)

type CSSProperty = `${string}-${string}`;
type ValidCSS = CSSProperty; // any string with a hyphen

Uniones discriminadas

Las uniones discriminadas son el patrón avanzado más importante para modelar el estado:

type RequestState<T> =
  | { status: "idle" }
  | { status: "loading" }
  | { status: "success"; data: T }
  | { status: "error"; error: string };

function handleState(state: RequestState<User>) {
  switch (state.status) {
    case "idle":
      return "Ready";
    case "loading":
      return "Loading...";
    case "success":
      return state.data.name; // TypeScript knows data exists
    case "error":
      return state.error;    // TypeScript knows error exists
  }
}

La propiedad status es el discriminante. TypeScript reduce el tipo automáticamente en cada rama.

Branded types

Los branded types evitan que se mezclen tipos que tienen la misma estructura:

type UserId = string & { readonly __brand: unique symbol };
type OrderId = string & { readonly __brand: unique symbol };

function branded<T extends string>(value: T): T & { readonly __brand: unique symbol } {
  return value as any;
}

const userId = branded<UserId>("user-123");
const orderId = branded<OrderId>("order-456");

function getUser(id: UserId) { /* ... */ }
getUser(userId);  // OK
getUser(orderId); // Error: OrderId is not assignable to UserId

Mejores prácticas

  • Utiliza utility types en lugar de duplicar manualmente las definiciones de tipos.
  • Prefiere las discriminated unions sobre las type assertions para el modelado de estados.
  • Construye los tipos a partir de piezas pequeñas y componibles en lugar de definiciones monolíticas y extensas.
  • Usa los conditional types con moderación; si un tipo es difícil de leer, simplifica la API.
  • Utiliza infer para extraer tipos de librerías en lugar de tipar manualmente los valores de retorno.
  • Mantén la fuente de verdad en un solo lugar y deriva todo lo demás.
  • Usa branded types cuando necesites nominal typing para IDs o valores similares.
  • Trata los tipos complejos como último recurso: los tipos simples son más fáciles de mantener.

Errores comunes

  • Sobre-diseñar los tipos cuando unos más simples funcionarían.
  • Crear tipos condicionales profundamente anidados que resultan imposibles de depurar.
  • Usar aserciones de as en lugar de un estrechamiento de tipos (type narrowing) adecuado.
  • No utilizar tipos de utilidad, lo que provoca la duplicación de definiciones de tipos.
  • Crear tipos demasiado complejos para el beneficio que aportan.
  • Olvidar que los tipos se eliminan en tiempo de ejecución; no existe la comprobación de tipos en runtime.
  • No documentar los tipos complejos con comentarios que expliquen su intención.

Qué aprender a continuación

Ahora ya comprendes los tipos avanzados: utility types, conditional types, mapped types y template literals. A partir de aquí, el siguiente paso natural es TypeScript para dominar los fundamentos: anotaciones, inferencia, interfaces y genéricos. Elige un proyecto, refactoriza algunos tipos y deja que la práctica haga el resto.

Utility types vs definiciones manuales

Los utility types derivan de tipos existentes, manteniendo una única fuente de verdad. Las definiciones manuales se duplican y se desincronizan.

Preferir
interface User {
  id: number;
  name: string;
  email: string;
}

type UserUpdate = Partial<User>;
type UserPreview = Pick<User, "id" | "name">;
Evitar
interface User {
  id: number;
  name: string;
  email: string;
}

interface UserUpdate {
  id?: number;
  name?: string;
  email?: string;
}

Uniones discriminadas vs aserciones de tipo

Las uniones discriminadas dan a TypeScript suficiente información para reducir los tipos automáticamente.

Preferir
type Result =
  | { ok: true; data: User }
  | { ok: false; error: string };

function handle(result: Result) {
  if (result.ok) {
    console.log(result.data);
  }
}
Evitar
interface Result {
  ok: boolean;
  data?: User;
  error?: string;
}

function handle(result: Result) {
  const user = result.data as User;
}

Preguntas frecuentes

Preguntas frecuentes

Keep learning

Related topics from the roadmap.

$ comienza a aprender

Listo para aprender Advanced Types?

Nuestro tutorial interactivo te guia a traves de Advanced Types paso a paso — con quizzes y codigo real que puedes ejecutar en el navegador.