嵌套类型的模板文字类型

Template literal type for a nested type

给定一个两层嵌套类型(层数不多也不少):

export type SomeNested = {
  someProp: {
    someChild: string
    someOtherChild: string
  }
  someOtherProp: {
    someMoreChildren: string
    whatever: string
    else: string
  }
}

我想要以下联合类型: "someProp.someChild" | "someProp.someOtherChild" | "someOtherProp.someMoreChildren" | "someOtherProp.whatever" | "someOtherProp.else".

我尝试了一些类似

的方法
type FirstLevel = keyof SomeNested
type SecondLevel = SomeNested[FirstLevel]
type Dot = `${FirstLevel}.${SecondLevel}`

但我可能遗漏了一些条件。 有人能给我指出正确的方向吗?

谢谢!

请注意,此题专门要求获取恰好包含两个级别的对象类型的虚线路径的并集。对于那些对任意深度对象的虚线路径并集感兴趣的人,您应该改为查看


通过 indexing into SomeNested with the union of its known keys type FirstLevel = keyof SomeNested,您得到了已知 属性 类型的联合:

type X = SomeNested[FirstLevel];
/* type X = {
    someChild: string;
    someOtherChild: string;
} | {
    someMoreChildren: string;
    whatever: string;
    else: string;
} */

并且该联合的键将只是出现在该联合的所有成员中的键(对于您的示例,这根本不是什么):

type SecondLevel = keyof SomeNested[FirstLevel] // never

基本上,通过创建这些联合,您已经丢弃了有关 SomeNested 的特定键与其特定 属性 类型之间的对应关系的信息。要修复它,您基本上需要遍历 SomeNested 的键并分别查看每个键的子属性。

一种方法是使用 mapped type,它可以让您遍历键,然后为每个键创建一个 属性。例如:

type Mapped = { [K in keyof SomeNested]:
  `${K}.${Extract<keyof SomeNested[K], string>}`
}
/* type Mapped = {
    someProp: "someProp.someChild" | "someProp.someOtherChild";
    someOtherProp: "someOtherProp.someMoreChildren" | 
      "someOtherProp.whatever" | "someOtherProp.else";
} */

Mapped 的每个 属性 都是一个 template literal type which starts with a particular key K from keyof SomeNested, followed by a dot, and then followed by the union of string keys from the corresponding property SomeNested[K]. (Ideally you could just write ${keyof SomeNested[K]}, but the compiler does not realize that these will definitely be strings for all possible K, so we can use the Extract<T, U> utility type 以说服编译器我们只查看 string 兼容键)。

当然你并不是真的想要 Mapped,一个与 SomeNested 具有相同键的对象类型。但是您确实需要其 属性 类型的并集,因此 现在 您可以使用其键的并集对其进行索引:

type Dot = { [K in keyof SomeNested]:
  `${K}.${Extract<keyof SomeNested[K], string>}`
}[keyof SomeNested]
/* type Dot = "someProp.someChild" | "someProp.someOtherChild" | 
  "someOtherProp.someMoreChildren" | "someOtherProp.whatever" | "someOtherProp.else" */

好了!

Playground link to code