-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathunflatten.ts
46 lines (39 loc) · 1.34 KB
/
unflatten.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import { RecursiveUnionToIntersection, Simplify } from '../typeHelpers'
/**
* ### unflatten(object)
*
* Unflattens an object with dot notation keys into a nested object.
*
* ```js
* flocky.unflatten({ 'a.b': 1, 'a.c': 2, 'd.e.f': 3 })
* // -> { a: { b: 1, c: 2 }, d: { e: { f: 3 } } }
* ```
*/
type UnflattenObject<TObject extends Record<string, unknown>> = {
[TKey in keyof TObject as TKey extends `${infer TKeyPrefix}.${string}`
? TKeyPrefix
: TKey]: TKey extends `${string}.${infer TKeySuffix}`
? UnflattenObject<{ [key in TKeySuffix]: TObject[TKey] }>
: TObject[TKey]
} extends infer TResult
? { [TResultKey in keyof TResult]: TResult[TResultKey] }
: never
export function unflatten<TObject extends Record<string, unknown>>(
object: TObject
): Simplify<RecursiveUnionToIntersection<UnflattenObject<TObject>>> {
const result: Record<string | number, unknown> = {}
for (const key in object) {
const keys = key.split('.')
let current = result
for (let i = 0; i < keys.length; i++) {
const part = keys[i]
if (i === keys.length - 1) {
current[part] = object[key]
} else {
if (!current[part]) current[part] = {}
current = current[part] as Record<string | number, unknown>
}
}
}
return result as Simplify<RecursiveUnionToIntersection<UnflattenObject<TObject>>>
}