Volver a TypeScript IntermedioLa Palabra Clave
Tipos Avanzados en TypeScript
El sistema de tipos de TypeScript es Turing-completo. Estas características avanzadas permiten modelar relaciones de datos complejas con precisión.
Tipos Mapeados
Transforma cada propiedad de un tipo:
type SoloLectura<T> = {
readonly [K in keyof T]: T[K];
};
type Opcional<T> = {
[K in keyof T]?: T[K];
};
type Anulable<T> = {
[K in keyof T]: T[K] | null;
};
// Personalizado: hacer campos específicos requeridos
type CamposRequeridos<T, K extends keyof T> = T & Required<Pick<T, K>>;
interface Usuario {
id?: number;
nombre?: string;
correo?: string;
}
type UsuarioConId = CamposRequeridos<Usuario, 'id'>; // id requerido, resto opcional
Tipos Condicionales
Tipos que dependen de una condición:
type EsArray<T> = T extends any[] ? true : false;
// EsArray<string[]> = true
// EsArray<string> = false
// Extraer el tipo del elemento de un array
type TipoElemento<T> = T extends (infer E)[] ? E : never;
type E = TipoElemento<string[]>; // string
// Desenvolver una Promise
type Esperado<T> = T extends Promise<infer R> ? Esperado<R> : T;
type A = Esperado<Promise<Promise<string>>>; // string
// Práctico: obtener tipo de retorno sin undefined
type SinUndefined<T> = T extends undefined ? never : T;
Tipos Literales de Plantilla
Compone tipos de cadena como template literals:
type NombreEvento = 'click' | 'focus' | 'blur';
type Manejador = `on${Capitalize<NombreEvento>}`; // 'onClick' | 'onFocus' | 'onBlur'
type PropiedadCSS = 'margin' | 'padding';
type DireccionCSS = 'top' | 'right' | 'bottom' | 'left';
type ClasesCSS = `${PropiedadCSS}-${DireccionCSS}`;
// 'margin-top' | 'margin-right' | ... | 'padding-left'
// Claves de eventos tipadas
type MapaEventos = {
'usuario:login': { idUsuario: string };
'usuario:logout': void;
'producto:agregado': { idProducto: string; cantidad: number };
};
type ClaveEvento = keyof MapaEventos; // 'usuario:login' | 'usuario:logout' | 'producto:agregado'
Uniones Discriminadas
type Resultado<T, E = Error> =
| { ok: true; valor: T }
| { ok: false; error: E };
async function obtenerUsuario(id: string): Promise<Resultado<Usuario>> {
try {
const usuario = await db.usuario.findUnique({ where: { id } });
if (!usuario) return { ok: false, error: new Error('No encontrado') };
return { ok: true, valor: usuario };
} catch (e) {
return { ok: false, error: e as Error };
}
}
const resultado = await obtenerUsuario('123');
if (resultado.ok) {
console.log(resultado.valor.nombre); // TypeScript sabe que valor existe
} else {
console.error(resultado.error.message); // TypeScript sabe que error existe
}
La Palabra Clave infer
Extrae tipos de otros tipos:
// Extraer tipos de parámetros de función
type Parametros<T extends (...args: any) => any> =
T extends (...args: infer P) => any ? P : never;
// Extraer tipo de retorno
type TipoRetorno<T extends (...args: any) => any> =
T extends (...args: any) => infer R ? R : never;
// Extraer tipo de resolución de Promise
type ValorPromesa<T> = T extends Promise<infer V> ? ValorPromesa<V> : T;
function saludar(nombre: string, edad: number) { return `Hola ${nombre}`; }
type ParamsSaludar = Parametros<typeof saludar>; // [string, number]
type RetornoSaludar = TipoRetorno<typeof saludar>; // string