给定一个两层嵌套类型(不超过、不少于两层):

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

我想要以下联合类型:

我试过类似的方法

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

但我可能错过了一些条件.

谢谢

推荐答案

注意,这个问题的目的是为一个正好由两个级别组成的对象类型获取虚线路径的并集.对于那些对任意深度对象的虚线路径并集感兴趣的人,你应该看看this question.


通过indexing into SomeNested及其known keys type FirstLevel = keyof SomeNested中的union,您将获得已知属性类型的并集:

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,它允许您在键上迭代,然后 for each 键创建一个属性.例如:

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,它以keyof SomeNested中的一个特定键K开始,后跟一个点,然后是对应属性SomeNested[K]中的字符串键的并集.(理想情况下,您可以只编写${keyof SomeNested[K]},但编译器没有意识到这些肯定是所有可能K的字符串,因此我们可以使用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

Typescript相关问答推荐

如何正确修复TypScript 4.7到4.8升级中的TS 2345编译错误

TypeScript将键传递给新对象错误

属性的类型,该属性是类的键,其值是所需类型

为什么TypeScript失go 了类型推断,默认为any?''

扩展函数签名中的参数类型,而不使用泛型

在TypeScrip中分配回调时,参数的分配方向与回调本身相反.为什么会这样呢?

创建一个根据布尔参数返回类型的异步函数

为什么T[数字]不也返回UNDEFINED?

如何在TypeScrip中正确键入元素和事件目标?

TypeScrip省略了类型参数,仅当传递另一个前面的类型参数时才采用默认类型

无法将从服务器接收的JSON对象转换为所需的类型,因此可以分析数据

从非文字数组(object.keys和array.map)派生联合类型

17个Angular 的延迟观察.如何在模板中转义@符号?

为什么TypeScrip假定{...省略类型,缺少类型,...选取类型,添加&>}为省略类型,缺少类型?

无法缩小深度嵌套对象props 的范围

推断集合访问器类型

两个子组件共享控制权

对于通过 Axios 获取的数据数组,属性map在类型never上不存在

是否有可能不允许在 TypeScript 中设置类属性,但也会抛出运行时错误?

通过函数将对象文字类型映射到另一种类型的方法或解决方法