Tipos Condicionales y Mapeados en Acción
Concepto clave
Los tipos condicionales y tipos mapeados son herramientas avanzadas de TypeScript que permiten crear tipos dinámicos basados en otros tipos. Piensa en ellos como "funciones" para tipos: mientras que las funciones transforman valores en tiempo de ejecución, estos tipos transforman tipos en tiempo de compilación.
Los tipos condicionales utilizan la sintaxis T extends U ? X : Y para crear tipos que dependen de una condición. Son como sentencias if/else para el sistema de tipos. Los tipos mapeados, por otro lado, transforman cada propiedad de un tipo existente usando la sintaxis {[K in keyof T]: ...}. Imagina que tienes un tipo que representa un objeto de configuración y necesitas crear una versión donde todas las propiedades sean opcionales o de solo lectura: los tipos mapeados son tu herramienta.
"Los tipos condicionales y mapeados son la base para construir utilidades de tipo reutilizables y arquitecturas de tipo flexibles en proyectos complejos."
Cómo funciona en la práctica
Veamos un ejemplo paso a paso de cómo combinar ambos conceptos. Supongamos que queremos crear un tipo que extraiga solo las propiedades de un objeto que sean funciones.
type FunctionProperties = {
[K in keyof T]: T[K] extends Function ? K : never
}[keyof T];
type ExtractFunctions = Pick>;
// Ejemplo de uso
interface UserActions {
id: number;
name: string;
save: () => void;
delete: (id: number) => boolean;
update: (data: Partial) => void;
}
type UserFunctions = ExtractFunctions;
// Resultado: { save: () => void; delete: (id: number) => boolean; update: (data: Partial) => void } Paso 1: FunctionProperties crea un tipo mapeado donde cada propiedad se evalúa con un tipo condicional. Si la propiedad es una función, devuelve el nombre de la propiedad (K), si no, devuelve never.
Paso 2: Al indexar con [keyof T], obtenemos una unión de solo los nombres de propiedades que son funciones.
Paso 3: ExtractFunctions usa Pick para crear un nuevo tipo con solo esas propiedades.
Caso de estudio
Imagina que estás construyendo una librería de validación para formularios. Necesitas tipos que automaticen la creación de interfaces para errores de validación basadas en los esquemas de formulario.
// Definimos un esquema de formulario
type FormSchema = {
email: string;
password: string;
age: number;
terms: boolean;
};
// Creamos un tipo que mapea cada propiedad a su tipo de error
type ValidationErrors = {
[K in keyof T]?: string | null;
};
// Tipo condicional para determinar si un formulario es válido
type IsValid = ValidationErrors extends { [K in keyof T]?: null }
? true
: false;
// Función que utiliza estos tipos
function validateForm(data: T, errors: ValidationErrors): IsValid {
const hasErrors = Object.values(errors).some(error => error !== null);
return !hasErrors as IsValid;
}
// Uso en código
const formData: FormSchema = {
email: "[email protected]",
password: "secret123",
age: 25,
terms: true
};
const errors: ValidationErrors = {
email: null, // sin error
password: "Debe tener al menos 8 caracteres",
age: null,
terms: null
};
const isValid = validateForm(formData, errors); // Tipo: false Este caso muestra cómo los tipos mapeados (ValidationErrors) y condicionales (IsValid) trabajan juntos para crear un sistema de tipos seguro para validación. La tabla siguiente resume las transformaciones:
| Tipo Original | Tipo Transformado | Transformación Aplicada |
|---|---|---|
| FormSchema | ValidationErrors | Cada propiedad se hace opcional y cambia a string | null |
| ValidationErrors | IsValid | Evalúa si todas las propiedades son null (true) o no (false) |
Errores comunes
- Confundir
extendscon asignación: En tipos condicionales,T extends Uverifica si T es asignable a U, no si son iguales. Para verificar igualdad exacta, usa[T] extends [U] ? ... : .... - Olvidar el operador
keyofen tipos mapeados: Siempre usa[K in keyof T]para iterar sobre propiedades.[K in T]no funciona porque T no es una unión de keys. - No manejar
neveradecuadamente: Cuando un tipo condicional devuelveneveren un tipo mapeado, esa propiedad se excluye del tipo resultante. Úsalo intencionalmente para filtrar propiedades. - Abusar de la complejidad: Los tipos condicionales anidados pueden volverse ilegibles rápidamente. Extrae lógica compleja en tipos auxiliares con nombres descriptivos.
- Ignorar distributividad: Los tipos condicionales sobre uniones son distributivos por defecto. Si necesitas tratar la unión como un todo, envuélvela en un array:
[T] extends [U] ? ... : ....
Checklist de dominio
- Puedo explicar la diferencia entre
T extends Uy[T] extends [U]en tipos condicionales - Sé crear un tipo mapeado que transforme todas las propiedades de un tipo (ej: hacerlas opcionales, de solo lectura, o cambiar sus tipos)
- Puedo combinar tipos condicionales y mapeados para filtrar propiedades basado en condiciones
- Entiendo cómo usar
neveren tipos mapeados para excluir propiedades no deseadas - Puedo crear tipos utilitarios reutilizables usando estos conceptos (ej:
Nullable,PartialBy) - Sé identificar y corregir errores de distributividad en tipos condicionales
- Puedo aplicar estos patrones en escenarios reales como validación, normalización de datos, o APIs tipo-safe
Implementa un sistema de permisos tipo-safe
Crea un sistema de permisos para una aplicación donde diferentes roles de usuario tienen acceso a distintas funcionalidades. Sigue estos pasos:
- Define un tipo
Rolecon los valores: 'admin', 'editor', 'viewer'. - Crea un tipo
Permissionque represente las acciones posibles: 'create', 'read', 'update', 'delete'. - Implementa un tipo mapeado
RolePermissionsque asocie cada rol con un conjunto de permisos (usa un tipo de unión). Por ejemplo: admin tiene todos los permisos, editor tiene ['create', 'read', 'update'], viewer solo ['read']. - Crea un tipo condicional
HasPermissionque verifique si un rol específico tiene un permiso determinado. Debe devolvertrueofalsecomo tipo literal. - Implementa una función
checkPermissionque acepte un rol y un permiso, y devuelva un booleano tipado conHasPermission. - Agrega validación para que si se pasa un permiso no válido para un rol, TypeScript muestre un error en tiempo de compilación.
Entrega el código completo con ejemplos de uso que demuestren tanto casos válidos como inválidos.
Pistas
- Usa un tipo de unión para los permisos de cada rol, ej: type AdminPermissions = Permission[]
- Para el tipo condicional, necesitaras verificar si el permiso esta incluido en la union de permisos del rol
- Considera usar tipos auxiliares para hacer el codigo mas legible
Evalua tu comprension
Completa el quiz interactivo de arriba para ganar XP.