20

Better types when extracting the keys and entries from an Object or an Enum

Monday, November 16, 2020

We probably all have used Object.keys(...) function at some point. Most of the time we are OK with the returned value of string[], but what if we want it to be more specific? For example, if we have a string enum, we'd really like to pick by a specific key later on.

For that purpose, I use the following two functions that wrap the built-in Object.keys() and Object.entries() and produce more specific types.

Code Examples

Better typed Object.keys

/**
 * Extracts typed keys instead of string.
 * For example if we have a specific set of keys (e.g. from string enum) we
 * want them to be properly typed
 * ```ts
 * enum AB {
 *   A = 'a',
 *   B = 'b',
 * }
 * type AbConfig = {
 *   // 👇 key is one of the enum values
 *   [aOrB in AB]?: boolean;
 * };
 *
 * const abConfig: AbConfig = {
 *   [AB.A]: true,
 * }
 *
 * Object.keys(abConfig); // type string[]
 * objectKeys(abConfig); // type AB[]
 * ```
 */
export function objectKeys<T extends object>(object: T): Array<keyof T> {
  return Object.keys(object) as Array<keyof T>;
}

Better typed Object.entries

/**
 * Extracts typed [key, value] array instead of [string, value]
 * * ```ts
 * enum AB {
 *   A = 'a',
 *   B = 'b',
 * }
 * type AbConfig = {
 *   // 👇 key is one of the enum values
 *   [aOrB in AB]?: boolean;
 * };
 *
 * const abConfig: AbConfig = {
 *   [AB.A]: true,
 * }
 *
 * Object.entries(abConfig); // type Array<[string, boolean]>
 * objectEntries(abConfig); // type Array<[AB, boolean]>
 * ```
 */
export function objectEntries<
  O extends object,
  K = keyof O,
  V = O extends {[key: string]: infer L} ? L : never
>(object: O): Array<[K, V]> {
  return Object.entries(object) as unknown as Array<[K, V]>;
}
Alex Okrushko

Principal UI Architect at Cisco (ex-Google, Firebase) NgRx team @AngularToronto organizer

Discussions are healthy ❤️