Skip to content

Instantly share code, notes, and snippets.

@wolever
Created December 2, 2024 00:08
Show Gist options
  • Save wolever/c921b5c1c05387925acd029cc9427ece to your computer and use it in GitHub Desktop.
Save wolever/c921b5c1c05387925acd029cc9427ece to your computer and use it in GitHub Desktop.
Type-safe definitions of `Object.keys`, `Object.entries`, and `Object.values`: `objectKeys`, `objectEntries`, and `objectValues`
/**
* ObjectType matches any "regular" object.
*
* For example:
* > {} satisfies ObjectType
* > { a: 1, b: 2 } satisfies ObjectType
* > [] satisfies ObjectType
* > null satisfies ObjectType
* TypeError: 'null' does not satisfy 'ObjectType'.
* > undefined satisfies ObjectType
* TypeError: 'undefined' does not satisfy 'ObjectType'.
* > 1 satisfies ObjectType
* TypeError: 'number' does not satisfy 'ObjectType'.
* > 'string' satisfies ObjectType
* TypeError: 'string' does not satisfy 'ObjectType'.
* > true satisfies ObjectType
* TypeError: 'boolean' does not satisfy 'ObjectType'.
*/
export type ObjectType = Record<PropertyKey, any>;
// Source: https://github.com/sindresorhus/ts-extras/blob/main/source/object-keys.ts
type ObjectKeys<T> = `${Exclude<keyof T, symbol>}`;
// Source: https://dev.to/harry0000/a-bit-convenient-typescript-type-definitions-for-objectentries-d6g
type TupleEntry<T extends readonly unknown[], I extends unknown[] = [], R = never> = T extends
readonly [infer Head, ...infer Tail] ? TupleEntry<Tail, [...I, unknown], R | [`${I['length']}`, Head]>
: R;
type ObjectEntry<T> = T extends object
? { [K in keyof T]: [K, Required<T>[K]] }[keyof T] extends infer E
? E extends [infer K, infer V] ? K extends string | number ? [`${K}`, V]
: never
: never
: never
: never;
type Entry<T> = T extends readonly [unknown, ...unknown[]] ? TupleEntry<T>
: T extends ReadonlyArray<infer U> ? [`${number}`, U]
: ObjectEntry<T>;
/**
* A strongly-typed version of `Object.keys()`.
*/
export const objectKeys = Object.keys as <T extends ObjectType>(value: T) => Array<ObjectKeys<T>>;
/**
* A strongly-typed version of `Object.entries()`.
*/
export const objectEntries = Object.entries as <T extends ObjectType>(value: T) => ReadonlyArray<Entry<T>>;
/**
* A strongly-typed version of `Object.values()`.
*/
export const objectValues = Object.values as <T extends ObjectType, K extends keyof T>(value: T) => ReadonlyArray<T[K]>;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment