Keyof嵌套子对象

时间:2017-08-30 23:14:39

标签: typescript

我有一个递归类型的对象,我想获取某个类型的键和任何子键。

例如。下面我想得到一个联合类型:

'/another' | '/parent' | '/child'

示例:

export interface RouteEntry {
    readonly name: string,
    readonly nested : RouteList | null
}
export interface RouteList {
    readonly [key : string] : RouteEntry
}

export const list : RouteList = {
    '/parent': {
        name: 'parentTitle',
        nested: {
            '/child': {
                name: 'child',
                nested: null,
            },
        },
    },
    '/another': {
        name: 'anotherTitle',
        nested: null
    },
}

在打字稿中,您可以使用keyof typeof RouteList来获取联合类型:

'/another' | '/parent' 

是否还有一种方法可以包含嵌套类型

3 个答案:

答案 0 :(得分:6)

这是一个艰难的。 TypeScript缺少 mapped conditional types general recursive type definitions,这两者都是我想要用来为你提供联合类型的东西。 (编辑2019-04-05:conditional types在TS2.8中被引入)你想要的东西有一些难点:

  • nested的{​​{1}}属性有时可能是RouteEntry,而评估为nullkeyof null的类型表达式会开始破坏。一个人需要小心。我的解决方法是添加一个虚拟密钥,使其永远不为空,然后在最后删除它。
  • 您使用的任何类型别名(称为null[keyof null])似乎都需要根据自身定义,并且您将收到“循环引用”错误。一种解决方法是提供一些可以达到某种有限嵌套水平的东西(例如,9级深度)。这可能会导致编译器减慢速度,因为它可能急切地评估所有9个级别,而不是将评估推迟到以后。
  • 这需要很多涉及映射类型的类型别名组合,并且有一个bug具有组合映射类型,在TypeScript 2.6之前不会修复。 workaround涉及使用通用默认类型参数。
  • “在末尾删除虚拟密钥”步骤涉及名为Diff的类型操作,该操作至少需要TypeScript 2.4才能正常运行。

这意味着:我有一个有效的解决方案,但我警告你,这是复杂而疯狂的。在我放入代码之前的最后一件事:你需要改变

RouteListNestedKeys<X>

export const list: RouteList = { // ...

即,从export const list = { // ... 变量中删除类型注释。如果您将其指定为list,则会丢弃TypeScript对RouteList的确切结构的了解,除了list之外,您将只获得任何密钥类型。通过省略注释,您可以让TypeScript推断出类型,因此它将记住整个嵌套结构。

好的,这里有:

string

让我们试一试:

type EmptyRouteList = {[K in 'remove_this_value']: RouteEntry};
type ValueOf<T> = T[keyof T];
type Diff<T extends string, U extends string> = ({[K in T]: K} &
  {[K in U]: never} & { [K: string]: never })[T];
type N0<X extends RouteList> = keyof X
type N1<X extends RouteList, Y = {[K in keyof X]: N0<X[K]['nested'] & EmptyRouteList>}> = keyof X | ValueOf<Y> 
type N2<X extends RouteList, Y = {[K in keyof X]: N1<X[K]['nested'] & EmptyRouteList>}> = keyof X | ValueOf<Y> 
type N3<X extends RouteList, Y = {[K in keyof X]: N2<X[K]['nested'] & EmptyRouteList>}> = keyof X | ValueOf<Y> 
type N4<X extends RouteList, Y = {[K in keyof X]: N3<X[K]['nested'] & EmptyRouteList>}> = keyof X | ValueOf<Y> 
type N5<X extends RouteList, Y = {[K in keyof X]: N4<X[K]['nested'] & EmptyRouteList>}> = keyof X | ValueOf<Y> 
type N6<X extends RouteList, Y = {[K in keyof X]: N5<X[K]['nested'] & EmptyRouteList>}> = keyof X | ValueOf<Y> 
type N7<X extends RouteList, Y = {[K in keyof X]: N6<X[K]['nested'] & EmptyRouteList>}> = keyof X | ValueOf<Y> 
type N8<X extends RouteList, Y = {[K in keyof X]: N7<X[K]['nested'] & EmptyRouteList>}> = keyof X | ValueOf<Y> 
type N9<X extends RouteList, Y = {[K in keyof X]: N8<X[K]['nested'] & EmptyRouteList>}> = keyof X | ValueOf<Y> 
type RouteListNestedKeys<X extends RouteList, Y = Diff<N9<X>,'remove_this_value'>> = Y;

如果您检查export const list = { '/parent': { name: 'parentTitle', nested: { '/child': { name: 'child', nested: null, }, }, }, '/another': { name: 'anotherTitle', nested: null }, } type ListNestedKeys = RouteListNestedKeys<typeof list> ,您会看到它是ListNestedKeys,如您所愿。这取决于你是否值得。

呼!希望有所帮助。祝你好运!

答案 1 :(得分:2)

这是一个无限递归的解决方案:

type Paths<T> = T extends RouteList
  ? keyof T | { [K in keyof T]: Paths<T[K]['nested']> }[keyof T]
  : never

type ListPaths = Paths<typeof list> // -> "/parent" | "/another" | "/child"

在Typescript v3.5.1上测试。另外,您还需要按照@jcalz的建议,从list变量中删除类型注释。

答案 2 :(得分:0)

@Aleksi 的回答展示了如何获取请求的类型联合 "/parent" | "/another" | "/child"

由于问题涉及路线层次结构,我想补充一点,从 typescript 4.1 开始,功能 Template Literal Types 不仅可用于获取所有密钥,还可以用于生成所有可能的路线"/parent" | "/another" | "/parent/child"

type ValueOf<T> = T[keyof T]
type AllPaths<T extends RouteList, Path extends string = ''> = ValueOf<{
    [K in keyof T & string]: 
        T[K]['nested'] extends null 
        ? K 
        : (`${Path}${K}` | `${Path}${K}${AllPaths<Extract<T[K]['nested'], RouteList>>}`)
}>

type AllRoutes = AllPaths<typeof list> // -> "/parent" | "/another" | "/parent/child"

与其他答案一样,list 对象可能没有 RouteList 类型注释,因为它会删除我们的 AllPaths 类型操作的类型信息。