Skip to content

Instantly share code, notes, and snippets.

@epranka
Created January 30, 2024 12:28
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 epranka/8cfc3053f8bfe1211424d47f3628c345 to your computer and use it in GitHub Desktop.
Save epranka/8cfc3053f8bfe1211424d47f3628c345 to your computer and use it in GitHub Desktop.
Merge Dotted Notation objects
// Type definitions
type Split<S extends string, D extends string> = string extends S
? string[]
: S extends ''
? []
: S extends `${infer T}${D}${infer U}`
? [T, ...Split<U, D>]
: [S];
type NestedObject<T, U = any> = T extends [infer F, ...infer R]
? { [K in F & string]: NestedObject<R, U> }
: U;
// Recursive type for deep merging
type DeepMergeObjects<T, U> = T extends object
? U extends object
? {
[K in keyof T | keyof U]: K extends keyof T
? K extends keyof U
? DeepMergeObjects<T[K], U[K]>
: T[K]
: K extends keyof U
? U[K]
: never;
}
: T
: T;
// Type for merging all members of a union
type DeepMergeUnion<T> = (
T extends any ? (k: DeepMergeObjects<T, T>) => void : never
) extends (k: infer I) => void
? I
: never;
type DeepMerge<T> = DeepMergeUnion<T[keyof T]>;
type ToNestedObject<T> = {
[P in keyof T]: P extends string ? NestedObject<Split<P, '.'>, T[P]> : never;
};
type Values = {
"a.b.c": string;
"a.b.d": number;
"a.b.e": boolean;
"a.f": string;
"a.g": number;
};
type Merged = DeepMerge<ToNestedObject<Values>>;
/**
* Result
*
* type Merged = {
* a: {
* b: {
* c: string;
* d: number;
* e: boolean;
* };
* f: string;
* g: number;
* }
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment