Patrones
¿Por qué `Object.keys(config)` me da `string[]` en vez de las claves que veo ahí mismo?
Porque un valor puede tener más claves que las que su tipo declara, y `Object.keys` las devuelve todas. Acotar el retorno es una decisión deliberada y local — no el arreglo de un fallo.
La receta
type Keys<T extends object> = (keyof T)[] const typedKeys = <T extends object>(value: T): Keys<T> => Object.keys(value) as Keys<T> const config = { retries: 3, verbose: true } const keys = typedKeys(config)
La compilación compila esto y comprueba cada resultado de abajo.
Cómo funciona
- 01
type Keys<T extends object> = (keyof T)[]
La forma que desearías que tuviera `Object.keys`. Nombrarla aparte mantiene el cast en un solo sitio y legible.
- 02
Object.keys(value) as Keys<T>Un cast, y honesto: estás afirmando que el objeto no tiene claves más allá de su tipo. Para un literal que acabas de construir es cierto; para algo que llegó de fuera puede no serlo.
Lo que obtienes
typeof keys→("retries" | "verbose")[]Keys<{ a: 1; b: 2 }>→("a" | "b")[](typeof keys)[number]
→"retries" | "verbose"E indexar por `number` recupera la unión, que suele ser sobre lo que querías iterar.
Dónde falla
El cast es mentira siempre que el objeto venga de fuera de tu programa. Las propiedades extra son legales — un valor tipado `{ a: 1 }` puede llevar `b` en ejecución — y este ayudante te dará una lista de claves sin ella afirmando estar completa.
Conclusión
Que `Object.keys` devuelva `string[]` no es un error de diseño; es el sistema de tipos siendo honesto sobre las propiedades extra. Usa este ayudante en objetos que construiste, no en los que recibiste.