typescript - 通过递归对象结构生成表示路径联合的元组类型
问题描述
下面是几个模板化的“业务”类型,分别用于表示复合状态和原子状态。
interface CompoundState<TName extends string, TChildren extends { [key: string]: AnyCompoundState | AnyAtomicState }> {
type: 'parent'
name: TName,
children: TChildren,
};
type AnyCompoundState = CompoundState<string, { [key: string]: AnyCompoundState | AnyAtomicState }>;
interface AtomicState<TName extends string> {
type: 'child',
name: TName,
}
type AnyAtomicState = AtomicState<string>;
在我的应用程序中,这些类型将被组合以产生复合和原子状态的树状结构。以下是此类类型的示例:
type MyStateChart = CompoundState<'cs0', {
cs1: CompoundState<'cs1', {
as1: AtomicState<'as1'>,
as2: AtomicState<'as2'>,
}>
}>;
我想要完成的是生成一个元组联合来表示 type 暗示的可能的“路径” MyStateChart
。可能的路径是元组,例如:
['cs0']
- a 的有效路径CompoundState
可能会或可能不会遍历到子级。['cs0', 'cs1']
- 和上面一样,我们不需要遍历叶子节点。['cs0', 'cs1', 'as1']
- 全深度['cs0', 'cs1', 'as2']
- 全深度
在实现这一目标的(大部分)失败尝试中,我采取了两种方法:
方法一:
type PathA<TNode extends AnyCompoundState | AnyAtomicState> = TNode extends AnyCompoundState
? {
[K in keyof TNode['children']]: [TNode['name']] | [TNode['name'], PathA<TNode['children'][K]>]
}[keyof TNode['children']]
: [TNode['name']]
// Produces a type represented by nested tuple unions. I have been unable to 'flatten' this into distinct, fully-realized tuples
type TestPathA = PathA<MyStateChart>;
这会产生一种非常接近我想要的类型但我无法“扁平化”的类型:
type TestPathA = ["cs0"] | ["cs0", ["cs1"] | ["cs1", ["l1"]] | ["cs1", ["l2"]]]
方法二:
type Cons<H, T extends unknown[]> = ((h: H, ...tail: T) => unknown) extends ((...args: infer U) => unknown) ? U : never;
// Approach B: Approach that I hoped would work but complains with:
type PathB<TNode extends AnyCompoundState | AnyAtomicState> = TNode extends AnyCompoundState
? {
[K in keyof TNode['children']]: [TNode['name']] | Cons<TNode['name'], PathB<TNode['children'][K]>>
}[keyof TNode['children']]
: [TNode['name']]
type TestPathB = PathB<MyStateChart>;
这种方法似乎是无限的,TypeScript 编译器抱怨:
"Type instantiation is excessively deep and possibly infinite.(2589)"
我能达到我想要的吗?如果有怎么办?
解决方案
正如@jcalz 在他的评论中指出的那样,这个问题可以通过与这个问题的答案相同的方法来解决。
这是应用于上述问题的样子:
type Cons<H, T> = T extends readonly any[] ?
((h: H, ...t: T) => void) extends ((...r: infer R) => void) ? R : never
: never;
type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ...0[]]
type Paths<T extends AnyAtomicState | AnyCompoundState, D extends number = 10> = [D] extends [never] ? never : T extends AnyCompoundState ?
{ [K in keyof T['children']]-?: [T['name']] | (Paths<T['children'][K], Prev[D]> extends infer P ?
P extends [] ? never : Cons<T['name'], P> : never
) }[keyof T['children']]
: [T['name']];
type TestC = Paths<MyStateChart>;
产生以下类型:
type TestC = ["cs0"] | ["cs0", "cs1"] | ["cs0", "cs1", "l1"] | ["cs0", "cs1", "l2"]
推荐阅读
- vue.js - 如何从状态 vuex 映射对象中的数组?
- c++ - 如何创建一个大小为 NxN 的方形二维数组,其中 N 由用户提供并填充一定范围内的随机数?
- html - 身体标签的背景图片不可见
- assembly - 汇编中数据段变量的绝对地址是什么?
- description-logic - 描述 需要对两个个体的相同性或差异性进行建模的逻辑表示
- react-native - 升级到 SDK 39 后使用 expo:build for .apk 时资产未显示
- c# - WaitAndRetryPolicy 与 BulkheadPolicy 结合,优先考虑重试。可能吗?
- php - 如何更改特定 php 文件的 php ini 设置
- python - 使用哈希值转换数据框列
- python - 如何编写正则表达式来修复由重复字母组成的单词?