CAP 12 · LEC 02·TypeScript avanzado

keyof, typeof y as const en profundidad

keyof, typeof y as const son tres operadores que conectan el sistema de tipos con el mundo en runtime. Combinados, permiten construir tipos ultra-precisos derivados directamente de tus valores.

● AVANZADO10 min lectura4 ejerciciospor Fernando Herrera · actualizado mayo de 2026
¿Encontraste un error o algo que mejorar?Editá esta lección en GitHub →

keyof T — union de claves de un tipo

keyof T produce una union de todos los nombres de propiedad de un tipo. Es la base para escribir funciones que aceptan solo claves válidas de un objeto.

interface Producto { id: number; nombre: string; precio: number; stock: number; } // keyof Producto = "id" | "nombre" | "precio" | "stock" type ClavesProducto = keyof Producto; // Función que solo acepta claves válidas del tipo T function obtener<T, K extends keyof T>(obj: T, clave: K): T[K] { return obj[clave]; } const prod: Producto = { id: 1, nombre: "Monitor", precio: 299, stock: 10 }; const nombre = obtener(prod, "nombre"); // string ← TypeScript infiere T[K] const precio = obtener(prod, "precio"); // number // obtener(prod, "descripcion"); // ❌ Error: "descripcion" no existe console.log(nombre); // "Monitor" console.log(precio); // 299 // Ordenar un array por cualquier clave numérica function ordenarPor<T>(arr: T[], clave: keyof T): T[] { return [...arr].sort((a, b) => a[clave] < b[clave] ? -1 : a[clave] > b[clave] ? 1 : 0 ); } const productos = [ { id: 3, nombre: "Teclado", precio: 89 }, { id: 1, nombre: "Mouse", precio: 29 }, { id: 2, nombre: "Monitor", precio: 299 }, ]; const porPrecio = ordenarPor(productos, "precio"); console.log(porPrecio.map(p => p.nombre)); // ["Mouse", "Teclado", "Monitor"]
SalidaMonitor 299 ["Mouse", "Teclado", "Monitor"]

typeof en TypeScript — inferir tipo de un valor

En TypeScript, typeof en contexto de tipo (no de expresión) devuelve el tipo de una variable o valor. Fundamental para trabajar con objetos cuyo tipo no has declarado explícitamente.

// typeof como expresión (JavaScript runtime) → string const x = typeof 42; // "number" const y = typeof "hola"; // "string" // typeof como tipo (TypeScript compile-time) → el tipo del valor const configuracion = { apiUrl: "https://api.ejemplo.com", timeout: 5000, reintentos: 3, debug: false, }; // Inferimos el tipo del objeto sin declararlo manualmente type Configuracion = typeof configuracion; // { // apiUrl: string; // timeout: number; // reintentos: number; // debug: boolean; // } function cargarConfig(override: Partial<typeof configuracion>): typeof configuracion { return { ...configuracion, ...override }; } const devConfig = cargarConfig({ debug: true, timeout: 10000 }); console.log(devConfig.apiUrl); // "https://api.ejemplo.com" // typeof también funciona con funciones function sumar(a: number, b: number): number { return a + b; } type FnSumar = typeof sumar; // (a: number, b: number) => number // Puedes crear una variable del mismo tipo que una función let operacion: FnSumar = sumar; operacion = (a, b) => a * b; // también válido — cumple la misma firma
Salidahttps://api.ejemplo.com

keyof typeof obj — union de claves en runtime

La combinación keyof typeof obj es el patrón más frecuente para extraer las claves de un objeto en runtime como un tipo union, sin declarar una interfaz separada.

const COLORES = { rojo: "#EF4444", verde: "#10B981", azul: "#3B82F6", morado: "#7C3AED", amarillo: "#F59E0B", }; // keyof typeof COLORES = "rojo" | "verde" | "azul" | "morado" | "amarillo" type NombreColor = keyof typeof COLORES; function obtenerHex(color: NombreColor): string { return COLORES[color]; } console.log(obtenerHex("morado")); // "#7C3AED" // obtenerHex("naranja"); // ❌ Error en compilación // Patrón común: validar claves en runtime con el tipo derivado del objeto const ENDPOINTS = { usuarios: "/api/usuarios", productos: "/api/productos", categorias: "/api/categorias", auth: "/api/auth", }; type Endpoint = keyof typeof ENDPOINTS; async function fetchEndpoint(endpoint: Endpoint): Promise<Response> { const url = ENDPOINTS[endpoint]; console.log(`GET ${url}`); return fetch(url); } // Solo acepta claves válidas — autocompletado incluido // fetchEndpoint("usuarios"); // fetchEndpoint("auth"); // fetchEndpoint("pedidos"); // ❌ Error // Función de validación en runtime que sincroniza con el tipo function esEndpointValido(valor: string): valor is Endpoint { return valor in ENDPOINTS; }
Salida#7C3AED

as const — literal types y objetos readonly

as const transforma un objeto para que TypeScript infiera tipos literales en lugar de tipos generales, y convierte todas las propiedades en readonly. El resultado es el tipo más preciso posible.

// Sin as const — TypeScript infiere tipos amplios const coloresSin = { primario: "#7C3AED", // string — demasiado amplio secundario: "#A78BFA", // string }; // Con as const — TypeScript infiere el valor literal exacto const colores = { primario: "#7C3AED", // "#7C3AED" — tipo literal secundario: "#A78BFA", // "#A78BFA" — tipo literal } as const; // typeof colores = { // readonly primario: "#7C3AED"; // readonly secundario: "#A78BFA"; // } // Arrays también se benefician const DIFICULTADES = ["easy", "medium", "hard", "master"] as const; // readonly ["easy", "medium", "hard", "master"] type Dificultad = (typeof DIFICULTADES)[number]; // "easy" | "medium" | "hard" | "master" function esDificultad(val: string): val is Dificultad { return (DIFICULTADES as readonly string[]).includes(val); } console.log(esDificultad("hard")); // true console.log(esDificultad("ultra")); // false // Tuplas — as const fija la longitud y tipos posicionales const coordenadas = [40.416775, -3.703790] as const; // readonly [40.416775, -3.703790] type Latitud = (typeof coordenadas)[0]; // 40.416775 type Longitud = (typeof coordenadas)[1]; // -3.703790
Salidatrue false

Combinando — as const + keyof typeof para enums type-safe

La combinación de as const con keyof typeof es el reemplazo moderno de los enums de TypeScript. Es más flexible, más debuggeable en runtime y evita el código generado por los enums.

// Patrón de "enum" con as const — PREFERIBLE a enum en TypeScript moderno const Rol = { ADMIN: "admin", EDITOR: "editor", LECTOR: "lector", MODERADOR: "moderador", } as const; // El tipo de los valores type ValorRol = (typeof Rol)[keyof typeof Rol]; // "admin" | "editor" | "lector" | "moderador" // El tipo de las claves type ClaveRol = keyof typeof Rol; // "ADMIN" | "EDITOR" | "LECTOR" | "MODERADOR" interface Usuario { id: number; nombre: string; rol: ValorRol; // solo acepta los valores válidos } function puedeBorrar(usuario: Usuario): boolean { return usuario.rol === Rol.ADMIN || usuario.rol === Rol.MODERADOR; } const admin: Usuario = { id: 1, nombre: "Ana", rol: Rol.ADMIN }; const lector: Usuario = { id: 2, nombre: "Luis", rol: Rol.LECTOR }; console.log(puedeBorrar(admin)); // true console.log(puedeBorrar(lector)); // false // Ventaja sobre enum: el valor en runtime es un string legible console.log(Rol.ADMIN); // "admin" — no un número opaco como en un enum numérico // Combinar con Record para una tabla de permisos const PERMISOS: Record<ValorRol, string[]> = { admin: ["leer", "escribir", "borrar", "admin"], editor: ["leer", "escribir"], lector: ["leer"], moderador: ["leer", "escribir", "moderar"], }; console.log(PERMISOS[Rol.EDITOR]); // ["leer", "escribir"]
Salidatrue false admin ["leer", "escribir"]
Criterioenum de TypeScriptas const + keyof typeof
Valor en runtimeNúmero opaco (enum numérico) o string (string enum)Siempre el valor declarado, legible
Tree-shakingGenera IIFE en JS — difícil de eliminarObjeto simple — tree-shakeable
ExtensibilidadNo se puede extender en runtimeSe puede usar spread para extender
Type safetyExcelenteExcelente — equivalente

Practica