用于将 "flattened" 对象的键递归重新映射到嵌套键中的类型(使用模板文字)

Type for recursively remapping keys of "flattened" object into nested one (using template literals)

我正在尝试将类型表达为一个函数,该函数接受一个带有键的对象作为点分隔路径和 returns 一个新对象,其中所有子路径都被扩展。我无法理解如何在递归 'de-structuring' 它们的同时保留给定路径的原始类型。

// Dummy structure
const flattenedObject = {
    'd.a': 1,
    'd.b': true,
};

type FlattenedObject = typeof flattenedObject;

// Expected type (or union of sub-paths: `{ d: { a: number } } | { d: { b: boolean } }`)
interface NestedObject {
    d: {
        a: number;
        b: boolean;
    }
}

// Attempt
type MapNestedKeys<R, N extends keyof R = keyof R, N0 = N> = N0 extends string
  ? N0 extends `${infer K1}.${infer K2}`
    ? { [K in N as `${K1}`]: MapNestedKeys<R, N, K2> }
    : { [K in N as N0]: R[K] }
  : {};

// Gives union, but types for nested keys are now union on all key types
type Test = MapNestedKeys<FlattenedObject>;

Link to TS Playground

已更新

// Dummy structure
const flattenedObject = {
  'a.b.c.d.e': 1,
  'f.g.h.j.k': true,
};

type FlattenedObject = typeof flattenedObject;

type Values<T> = T[keyof T]

type MakeObj<Prop, Value> =
  Prop extends string ?
  Prop extends `${infer Head}.${infer Tail}` ?
  { [Key in Head]: MakeObj<Tail, Value> }
  : Prop extends string ? { [K in Prop]: Value }
  : never
  : never

type Iterate<Obj> = Values<{
  [Prop in keyof Obj]: MakeObj<Prop, Obj[Prop]>
}>

type Result = Iterate<FlattenedObject>

Playground

另一种方法可以更好地处理不同深度的路径(例如 'd.a.c')

type MapNestedKeys<T, P = keyof T> = 
   P extends `${infer K1}.${infer K2}` & keyof T ?
      MapNestedKeys<{[S in K1]: MapNestedKeys<{[S1 in K2]: T[P]}>}>  : 
   P extends string & keyof T ? {[S in P]: T[P]} : {}