TS:禁止描述状态机制的类型中的循环

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (1)
  • 关注 (0)
  • 查看 (3)

我在我的应用程序中实现了一个多步骤过程作为状态机,并创建了表示可能的状态转换的类型:

enum ProcessStep {
  STEP_1,
  STEP_2a,
  STEP_2b,
  STEP_3
}
type ValidNextStep<Step extends ProcessStep> = {
  [ProcessStep.STEP_1]:
    | ProcessStep.STEP_2a
    | ProcessStep.STEP_2b;
  [ProcessStep.STEP_2a]: ProcessStep.STEP_3;
  [ProcessStep.STEP_2b]: ProcessStep.STEP_3;
  [ProcessStep.STEP_3]: never;
}[Step]

但我希望能够判断我是否在此图中创建了一个循环,即是否ProcessStep.STEP_3可以转换回ProcessStep.STEP_2a

如何才能在类型级别建立这种不变量?看起来很难,因为默认类型别名不允许循环引用。

提问于
用户回答回答于

哇,我喜欢这个问题。我不确定是否有一种干净或正确的方法只使用类型系统来做到这一点。

这是一个不干净且可能不正确的方法:强制类型系统完成为每个状态启动状态机的计算并运行它以进行一些大量的步骤。如果每个可能的最终状态是never,则没有循环。否则,您可以选择循环,也可以在图表中使用非常长的非循环路径。

想象一下,你有一个M扩展的对象类型,这Record<keyof M, keyof M>意味着它的值M也是M。这描述了一个状态机(你在你的定义中有这样的类型,ValidNextStep但你通过索引它来销毁它......不用担心,我们可以重建它{ [K in ProcessStep]: ValidNextStep<K> })。对于任何键KM您可以计算M[K]一步,M[M[K]]两步或M[M[M[K]]]三步等。

我们可以非常快速地组成这些操作以获得疯狂的步骤:

type TwoSteps<M extends Record<keyof M, keyof M>> = { [K in keyof M]: M[M[K]] };
type FourSteps<M extends Record<keyof M, keyof M>> = TwoSteps<TwoSteps<M>>;
type SixteenSteps<M extends Record<keyof M, keyof M>> = FourSteps<FourSteps<M>>;
type TwoHundredFiftySixSteps<M extends Record<keyof M, keyof M>> = SixteenSteps<
  SixteenSteps<M>
>;

如果没有编译器对我大喊大叫,这就是我能得到的。

然后,如果检测到循环(或非常长的路径),我们可以创建一个导致编译器错误的见证类型:

type NoCycles<
  N extends never = TwoHundredFiftySixSteps<
    { [K in ProcessStep]: ValidNextStep<K> }
  >[ProcessStep]
> = true;

这对于您的原始定义很好ValidNextStep,但是如果我们将其更改为以下内容:

type ValidNextStep<Step extends ProcessStep> = {
  [ProcessStep.STEP_1]: ProcessStep.STEP_2a | ProcessStep.STEP_2b;
  [ProcessStep.STEP_2a]: ProcessStep.STEP_3;
  [ProcessStep.STEP_2b]: ProcessStep.STEP_3;
  [ProcessStep.STEP_3]: ProcessStep.STEP_2a; // oops
}[Step];

然后NoCycles定义产生以下错误:

// Type 'ProcessStep.STEP_2a | ProcessStep.STEP_3' does not satisfy the constraint 'never'.

这表明在运行机器256步后,它仍然可能处于STEP_2a或者STEP_3,这表示一个循环。

这是一个好主意吗?可能不是......它不能保证是正确的,它可能会给编译器带来比保证更多的压力。但我不知道我有多难找到更好的东西。使用通用语言,您可以尝试找到一种有效的算法来检测循环,但TypeScript中的类型系统不太可能足以表达它。

所以使用它需要您自担风险,祝您好运!

链接到代码

所属标签

可能回答问题的人

  • HKC

    红客学院 · 创始人 (已认证)

    26 粉丝7 提问5 回答
  • Dingda

    Dingda · 站长 (已认证)

    4 粉丝0 提问3 回答
  • 螃蟹居

    1 粉丝0 提问2 回答
  • 西风

    renzha.net · 站长 (已认证)

    9 粉丝1 提问2 回答

扫码关注云+社区

领取腾讯云代金券