Patterns
How do I preserve the relationship between each key and its value while iterating `Object.entries`?
Build a union of key/value tuples with a mapped type and keep the unavoidable assertion inside one helper.
The recipe
type Entry<T extends object> = { [K in keyof T]-?: readonly [K, T[K]] }[keyof T] function typedEntries<T extends object>(value: T): Entry<T>[] { return Object.keys(value).map(key => [key, value[key as keyof T]] as unknown as Entry<T> ) } const settings = { dark: true, retries: 3 } const entries = typedEntries(settings)
The build compiles this and checks each result below.
How it works
- 01
readonly [K, T[K]]Each mapped property becomes a tuple whose value is tied to that exact key.
- 02
}[keyof T]Indexing collapses the table into a discriminated union of entries.
What you get
readonly ['a', 1] extends Entry<{ a: 1; b: 'x' }> ? true : false
→truereadonly ['b', 'x'] extends Entry<{ a: 1; b: 'x' }> ? true : false
→truereadonly ['a', 'wrong'] extends Entry<{ a: 1; b: 'x' }> ? true : false
→false
Where it goes wrong
Like typed keys, the assertion assumes no extra runtime properties beyond `keyof T`. Use it for objects your code owns, not unvalidated values from APIs or JSON.
Takeaway
A mapped object indexed by `keyof` is a precise way to build a correlated tuple union.
