Object.Entries(obj) -- Object to Array of KeyValue Pairs
This one is relatively straightforward. Using [K in keyof OBJ_T]
you can get the keys, and OBJ_T[K]
gives the relative value.
Here is a simple implementation of it:
// ~~~~~~~~~~~~~~~~~~~~~~~~ Utils ~~~~~~~~~~~~~~~~~~~~~~~~
type ObjectType = Record<PropertyKey, unknown>;
type PickByValue<OBJ_T, VALUE_T> // From https://stackoverflow.com/a/55153000
= Pick<OBJ_T, { [K in keyof OBJ_T]: OBJ_T[K] extends VALUE_T ? K : never }[keyof OBJ_T]>;
type ObjectEntries<OBJ_T> // From https://stackoverflow.com/a/60142095
= { [K in keyof OBJ_T]: [keyof PickByValue<OBJ_T, OBJ_T[K]>, OBJ_T[K]] }[keyof OBJ_T][];
// ~~~~~~~~~~~~~~~~~~~~ Typed Function ~~~~~~~~~~~~~~~~~~~~
function getTypedObjectEntries<OBJ_T extends ObjectType>(obj: OBJ_T): ObjectEntries<OBJ_T> {
return Object.entries(obj) as ObjectEntries<OBJ_T>;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~ Test ~~~~~~~~~~~~~~~~~~~~~~~~~
const myOldObject = {
x: 6,
y: "apple",
z: true
};
const myTypelessEntries = Object.entries(myOldObject); // type: [string, string | number | boolean][]
const myTypedEntries = getTypedObjectEntries(myOldObject);
type myTypedEntiresType = typeof myTypedEntries; // type: (["x", number] | ["y", string] | ["z", boolean])[]
TS Playground. Note that you need es2019
to be the target in Configs.
Object.fromEntries(entries) -- Array of KeyValue Pairs to Object
This is one a bit more challenging.
You need to first extract the internal array pairs using infer
, and then merge the result using the common UnionToIntersection
utility type.
Here is the best I could come up with:
// ~~~~~~~~~~~~~~~~~~~~~~~~ Utils ~~~~~~~~~~~~~~~~~~~~~~~~
// Data Types
type EntriesType = [PropertyKey, unknown][] | ReadonlyArray<readonly [PropertyKey, unknown]>;
// Existing Utils
type DeepWritable<OBJ_T> = { -readonly [P in keyof OBJ_T]: DeepWritable<OBJ_T[P]> };
type UnionToIntersection<UNION_T> // From https://stackoverflow.com/a/50375286
= (UNION_T extends any ? (k: UNION_T) => void : never) extends ((k: infer I) => void) ? I : never;
// New Utils
type UnionObjectFromArrayOfPairs<ARR_T extends EntriesType> =
DeepWritable<ARR_T> extends (infer R)[] ? R extends [infer key, infer val] ? { [prop in key & PropertyKey]: val } : never : never;
type MergeIntersectingObjects<ObjT> = {[key in keyof ObjT]: ObjT[key]};
type EntriesToObject<ARR_T extends EntriesType> = MergeIntersectingObjects<UnionToIntersection<UnionObjectFromArrayOfPairs<ARR_T>>>;
// ~~~~~~~~~~~~~~~~~~~~~ Typed Functions ~~~~~~~~~~~~~~~~~~~~~
function createTypedObjectFromEntries<ARR_T extends EntriesType>(arr: ARR_T): EntriesToObject<ARR_T> {
return Object.fromEntries(arr) as EntriesToObject<ARR_T>;
}
// ~~~~~~~~~~~~~~~~ Test for entries->object ~~~~~~~~~~~~~~~~~
const myArrayOfPairs = [["a", 5], ["b", "hello"], ["c", false]] as const;
const myTypelessObject = Object.fromEntries(myArrayOfPairs); // type: any
const myTypedObject = createTypedObjectFromEntries(myArrayOfPairs);
type myTypedObjectType = typeof myTypedObject; // type: { a: 5; b: "hello"; c: false; }
TS Playground