Skip to content
webtype.orgGet typed keys from an object

    ↑↓ move · ⏎ open · esc close

    Patterns

    Why does `Object.keys(config)` give me `string[]` instead of the keys I can see right there?

    Because a value can have more keys than its type declares, and `Object.keys` returns all of them. Narrowing the return is a deliberate, local decision — not a fix for a bug.

    The recipe

    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)

    The build compiles this and checks each result below.

    How it works

    1. 01
      type Keys<T extends object> = (keyof T)[]

      The shape you wish `Object.keys` had. Naming it separately keeps the cast in one place and readable.

    2. 02
      Object.keys(value) as Keys<T>

      A cast, and an honest one: you are asserting the object has no keys beyond its type. For a literal you just built that is true; for something that arrived from elsewhere it may not be.

    What you get

    Where it goes wrong

    The cast is a lie whenever the object came from outside your program. Extra properties are legal — a value typed `{ a: 1 }` may carry `b` at run time — and this helper will hand you a key list that does not include it while claiming to be complete.

    Takeaway

    `Object.keys` returning `string[]` is not a design mistake; it is the type system being honest about extra properties. Use this helper on objects you built, not on ones you received.

    See also

    Patterns