构建具有特定结构的递归对象的键的联合。

huangapple go评论54阅读模式
英文:

Build union of keys in recursive object with specific shape

问题

以下是翻译好的代码部分:

type ActionUnion<T> = T extends { [K in keyof T]: infer U }
  ? U extends { states: any }
    ? ActionUnion<U['states']> // 递归调用用于嵌套状态
    : U extends { [key: string]: any }
      ? keyof Omit<U, 'initial' | 'states'> // 提取动作键
      : never
  : never;

type T1 = ActionUnion<typeof config>; // 应该是 "ACTION_1" | "ACTION_2" | 等等...

请注意,我已经移除了 HTML 实体编码(如 &lt;&quot;)以获得更清晰的代码。

英文:

I'm trying to extract the ACTION_ keys from the following config shape (it's a state machine). Notice that a state can have initial, states, then any number of actions:

export const config = {
  initial: &#39;STATE_1&#39;,
  states: {
    STATE_1: {
      ACTION_5: &#39;STATE_2&#39;,
      initial: &#39;STATE_3&#39;,
      states: {
        STATE_3: {
          ACTION_1: &#39;STATE_4&#39;,
          ACTION_2: &#39;STATE_2&#39;,
        },
        STATE_4: {
          ACTION_3: &#39;STATE_3&#39;,
        },
      },
    },

    STATE_2: {
      ACTION_4: &#39;STATE_1&#39;,
      ACTION_5: &#39;STATE_3&#39;,
      initial: &#39;STATE_5&#39;,
      states: {
        STATE_5: {
          ACTION_6: &#39;STATE_6&#39;,
        },
        STATE_6: {
          ACTION_6: &#39;STATE_5&#39;,
        },
      },
    },
  },
};

The best I can come up with at the moment is this:

type ActionUnion&lt;T&gt; = T extends { [K in keyof T]: infer U }
  ? U extends { states: any }
    ? ActionUnion&lt;U[&#39;states&#39;]&gt; // recursive call for nested states
    : U extends { [key: string]: any }
      ? keyof Omit&lt;U, &#39;initial&#39; | &#39;states&#39;&gt; // extract action keys
      : never
  : never;

But this is inferred as &quot;STATE_1&quot; | &quot;STATE_2&quot;.

type T1 = ActionUnion&lt;typeof config&gt;; // should be &quot;ACTION_1&quot; | &quot;ACTION_2&quot; | etc...

I think I'm probably making this more complex than it needs to be.

Playground Link

答案1

得分: 0

以下是代码部分的中文翻译:

// 工具函数:

// `Prettify` - 简化IDE显示的类型:
type Prettify<T> = T extends infer R ? { [K in keyof R]: R[K] } : never;

// 接下来,使用[映射类型](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html)遍历对象并检查其键是否以`ACTION_`开头,如果是,则选择该键,否则,如果属性是对象,则以相同的方式递归检查该属性的键,如果前述条件都不成立,则分配为`never`。然后,提取该对象的值以获得值的并集:
type ActionUnion<T extends object> = Prettify<{
  [K in keyof T]: K extends `ACTION_${string}`
    ? K
    : T[K] extends object
    ? ActionUnion<T[K]>
    : never;
}[keyof T]>;

// 用法示例:
// 类型 Actions = "ACTION_4" | "ACTION_5" | "ACTION_6" | "ACTION_3" | "ACTION_1" | "ACTION_2"
type Actions = ActionUnion<typeof config>;

您可以在playground中测试此代码。

请注意,我只翻译了代码部分,不包括代码之外的内容。

英文:

Utilities:

Prettify - simplifies the type shown by the IDE:

type Prettify&lt;T&gt; = T extends infer R ? { [K in keyof R]: R[K] } : never;

Next, using the mapped types we will map through the object and check if the key of it starts with ACTION_ then we pick the key, otherwise if the property is an object, we recursively check in the same manner the keys of that property, if none of the previous conditions are true, we just assign never. Then we extract the values of that object to get the union of values:

type ActionUnion&lt;T extends object&gt; = Prettify&lt;
  {
    [K in keyof T]: K extends `ACTION_${string}`
      ? K
      : T[K] extends object
      ? ActionUnion&lt;T[K]&gt;
      : never;
  }[keyof T]
&gt;;

Usage:

// type Actions = &quot;ACTION_4&quot; | &quot;ACTION_5&quot; | &quot;ACTION_6&quot; | &quot;ACTION_3&quot; | &quot;ACTION_1&quot; | &quot;ACTION_2&quot;
type Actions = ActionUnion&lt;typeof config&gt;;

playground

huangapple
  • 本文由 发表于 2023年6月4日 23:01:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/76401019.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定