首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >TS:不允许在描述状态机的类型中使用循环

TS:不允许在描述状态机的类型中使用循环
EN

Stack Overflow用户
提问于 2019-06-19 07:23:30
回答 1查看 901关注 0票数 1

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

代码语言:javascript
复制
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

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

EN

回答 1

Stack Overflow用户

发布于 2019-06-19 09:05:28

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

这里有一种不干净且可能不正确的方法:强制类型系统计算为每个状态启动状态机并运行它一些大量的步骤。如果每个可能的结束状态都是never,那么就没有循环。否则,要么是图中有圈,要么是图中有一些非常长的非循环路径。

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

我们可以非常快速地组合这些操作,以获得数量惊人的步骤:

代码语言:javascript
复制
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>
>;

如果编译器不对我大喊大叫,我只能做到这一步。

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

代码语言:javascript
复制
type NoCycles<
  N extends never = TwoHundredFiftySixSteps<
    { [K in ProcessStep]: ValidNextStep<K> }
  >[ProcessStep]
> = true;

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

代码语言:javascript
复制
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定义会产生以下错误:

代码语言:javascript
复制
// Type 'ProcessStep.STEP_2a | ProcessStep.STEP_3' does not satisfy the constraint 'never'.

这表明,在运行了256个步骤之后,仍然可以处于STEP_2aSTEP_3中,这表示一个周期。

这是个好主意吗?可能不会..。不能保证它是正确的,而且它可能会给编译器带来比保证更多的压力。但我不知道我想找更好的工作有多难。使用通用语言,您会尝试找到some efficient algorithm来检测循环,但TypeScript中的类型系统不太可能具有足够的表达能力来实现它。

因此,使用它的风险自负,祝你好运!

Link to code

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56658241

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档