Skip to content

Instantly share code, notes, and snippets.

@gabrielelana
Created May 25, 2023 16:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gabrielelana/fa6bc558efbf2975d846047c585a7c4e to your computer and use it in GitHub Desktop.
Save gabrielelana/fa6bc558efbf2975d846047c585a7c4e to your computer and use it in GitHub Desktop.
Type safe and preserving version of `Object.entries` and `Object.fromEntries`
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never;
type UnionToOvlds<U> = UnionToIntersection<
U extends any ? (f: U) => void : never
>;
type PopUnion<U> = UnionToOvlds<U> extends (a: infer A) => void ? A : never;
type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? false : true;
type UnionToArray<T, A extends unknown[] = []> = IsUnion<T> extends true
? UnionToArray<Exclude<T, PopUnion<T>>, [PopUnion<T>, ...A]>
: [T, ...A];
type ZipArray<X extends Array<any>, Y extends Array<any>, R extends Array<any> = []> =
X extends [infer HX, ...infer TX extends Array<any>]
? Y extends [infer HY, ...infer TY extends Array<any>]
? ZipArray<TX, TY, [...R, [HX, HY]]>
: R
: R;
type ValueOf<X extends Record<string, unknown>, K extends Array<keyof X>, R extends Array<unknown> = []> =
K extends [infer H extends string, ...infer T extends Array<keyof X>]
? ValueOf<X, T, [...R, X[H]]>
: R;
type Entry = readonly [string, unknown]
type Entries = readonly Entry[]
type FromEntriesToObject<E extends Entries, R = {}> =
E extends readonly [infer H extends Entry, ...infer T extends Entries]
? FromEntriesToObject<T, R & {[X in H as X[0]]: X[1]}>
: E extends readonly []
? R
: never;
type FromObjectToEntries<X extends Record<string, unknown>, K = UnionToArray<keyof X>> =
K extends Array<keyof X>
? ZipArray<K, ValueOf<X, K>>
: never;
function fromEntries<const T extends Entries>(entries: T): FromEntriesToObject<T>
function fromEntries(entries: Array<[string, unknown]>) {
return Object.fromEntries(entries);
}
function toEntries<const T extends Record<string, unknown>>(r: T): FromObjectToEntries<T>
function toEntries(r: any) {
return Object.entries(r);
}
// Prove the roundtrip
const x = {foo: 1, bar: undefined, baz: "baz"} as const
const y = toEntries(x)
const z: typeof x = fromEntries(y)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment