我有一个递归类型的对象,我想获取某些类型的键和任何子键.
例如.下面我想得到一个联合类型:
'/another' | '/parent' | '/child'
Run Code Online (Sandbox Code Playgroud)
例:
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
},
}
Run Code Online (Sandbox Code Playgroud)
在打字稿中,您可以使用keyof typeof RouteList来获取联合类型:
'/another' | '/parent'
Run Code Online (Sandbox Code Playgroud)
是否有一种方法也包括嵌套类型
这是一个艰难的过程。TypeScript 既没有映射的条件类型,也没有 通用的递归类型定义,这两种都是我想用来为您提供的联合类型。(编辑2019-04-05:条件类型在TS2.8中引入)有一些需要注意的问题:
nested
属性RouteEntry
有时可以是null
,并且类型表达式可以求值keyof null
或null[keyof null]
开始破坏事物。需要注意一点。我的解决方法是添加一个虚拟密钥,以使其永远不会为空,然后最后将其删除。RouteListNestedKeys<X>
),似乎都需要对其本身进行定义,并且会出现“循环引用”错误。一种解决方法是提供一些可以在有限的嵌套级别(例如9层深)中工作的对象。这可能会导致编译器变慢,因为它可能会急切地评估所有9个级别,而不是将评估推迟到以后。Diff
,该操作至少需要TypeScript 2.4才能正常运行。这意味着:我有一个可行的解决方案,但我警告您,它是复杂而疯狂的。在放入代码之前的最后一件事:您需要进行更改
export const list: RouteList = { // ...
Run Code Online (Sandbox Code Playgroud)
至
export const list = { // ...
Run Code Online (Sandbox Code Playgroud)
也就是说,从list
变量中删除类型注释。如果将其指定为RouteList
,那么您将丢掉TypeScript关于的确切结构的知识,list
除了string
键类型之外,您将一无所获。通过取消注释,可以让TypeScript推断类型,因此它将记住整个嵌套结构。
好的,这里是:
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;
Run Code Online (Sandbox Code Playgroud)
让我们尝试一下:
export const list = {
'/parent': {
name: 'parentTitle',
nested: {
'/child': {
name: 'child',
nested: null,
},
},
},
'/another': {
name: 'anotherTitle',
nested: null
},
}
type ListNestedKeys = RouteListNestedKeys<typeof list>
Run Code Online (Sandbox Code Playgroud)
如果您进行检查,ListNestedKeys
您会发现它是"parent" | "another" | "child"
所需的。是否值得这样做取决于您。
ew!希望能有所帮助。祝好运!
这是一个无限递归的解决方案:
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"
Run Code Online (Sandbox Code Playgroud)
在Typescript v3.5.1上测试。您还需要按照list
@jcalz的建议从变量中删除类型注释。
归档时间: |
|
查看次数: |
1975 次 |
最近记录: |