Mapped Types y Conditional Types
Los Mapped Types son como un map() de JavaScript pero para los tipos de TypeScript: aplican una transformación a cada propiedad de un tipo y crean uno nuevo. La sintaxis [K in keyof T] itera sobre cada clave K del tipo T, y defines qué hacer con ella. Es cómo están implementados Partial, Readonly y Required internamente en TypeScript.
Puedes incluso renombrar claves mientras mapeas, creando por ejemplo Getters<T> que convierte cada propiedad en un método getter.
Los Conditional Types aplican lógica: "si T extiende U, el tipo es X; de lo contrario, es Y". Es como un operador ternario (?:) pero para tipos. La palabra clave infer dentro de un conditional type permite extraer un subtipo: si T extends Promise<infer R>, TypeScript extrae el tipo R que está dentro de la Promise.
Con esto puedes construir tipos como PromiseResult<T> que, si T es una Promise, devuelve el tipo que resuelve, o T directamente si no lo es.
// Mapped Type — transforma cada propiedad
type Opcional<T> = { [K in keyof T]?: T[K] }; // = Partial<T>
type Inmutable<T> = { readonly [K in keyof T]: T[K] }; // = Readonly<T>
// Mapped type con transformación de clave
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
// Getters<{ nombre: string }> = { getNombre: () => string }
type Setters<T> = {
[K in keyof T as `set${Capitalize<string & K>}`]: (val: T[K]) => void;
};
// Conditional Types
type EsString<T> = T extends string ? true : false;
type R1 = EsString<string>; // true
type R2 = EsString<number>; // false
// Distributive conditional type
type Nullable<T> = T extends any ? T | null : never;
// Nullable<string | number> = string | null | number | null
// infer — extraer tipos dentro de conditional types
type ElementoDeArray<T> = T extends Array<infer Item> ? Item : never;
type Num = ElementoDeArray<number[]>; // number
type Str = ElementoDeArray<string[]>; // string
type PromiseResult<T> = T extends Promise<infer R> ? R : T;
type AsyncStr = PromiseResult<Promise<string>>; // string
type SyncNum = PromiseResult<number>; // number
// DeepPartial — recursivo
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};
Practica esta lección
Resuelve los ejercicios del curso de TypeScript en la app, gratis y con corrección automática.
Empezar a practicar