1

我不确定我是否在描述标题中当前的任务。我想问的来自以下要求。

我正在尝试为有限状态机的状态做一个抽象,并提出以下定义(在打字稿中)

interface IState {
    send<T, E>(message: T, callback?:(event: E)=>void): IState;
}

我试图表达有限状态机的状态应该能够接受消息并返回新状态,并带有一个可选的回调来处理转换期间的事件。

当将此接口实现为具体状态时,会出现问题。


例如,我正在尝试制作一个只有两种状态的简单状态机,LEFTRIGHT,并带有三个可能的消息go-onturn-leftturn-right。下表显示了它们的关系。

在此处输入图像描述

关键是我想限制状态LEFT只接受go-onturn-right消息,而 send turn-left to LEFT预计是编译错误

我尝试在 typescript 3.4.5 中实现如下。

class Left implements IState {
  send(m: 'go-on', cb?: (e: never) => void): Left;
  send(m: 'turn-right', cb?: (e: never) => void): Right;
  send(m: 'go-on' | 'turn-right', cb?: any) {
    return m === 'go-on' ? new Left() : new Right();
  }
}

class Right implements IState {
  send(m: 'go-on', cb?: (e: never) => void): Right;
  send(m: 'turn-left', cb?: (e: never) => void): Left;
  send(m: 'go-on' | 'turn-left', cb?: any) {
    return m === 'go-on' ? new Right() : new Left();
  }
}

该实现没有编译错误,并且自动完成按预期工作。但是因为它看起来很奇怪,所以我问了一个问题TypeScript function generic can only work for function overload with more than one signatures

感谢您在该问题下的友好回复,我了解将重载函数分配给泛型函数是错误的。但是,如何在保持特定状态只接受所需类型的消息的同时表达状态的一般接口呢?

相关的 GitHub 问题


我能想到的另一个抽象是

interface IState<T, E, R extends IState<?, ?, ?>> {
    send(message: T, callback?:(event: E)=>void): R;
}

但是返回类型是递归的,我不知道上面那三个问号要填什么。

一个更简单的版本可能是

interface IState<T, E> {
    send(message: T, callback?:(event: E)=>void): IState<any, any>;
}

除了返回类型中令人讨厌的any之外,它的行为似乎很像。

interface IState {
    send<T, E>(message: T, callback?:(event: E)=>void): IState;
}

在 GitHub 中发现了一个关于 generic value 的可能相关问题


这个问题定义明确吗?

如果属实,上面列出的这些方法是否有正确的解决方案?

如果为假,正确的解决方案是什么?

4

1 回答 1

2

我认为最好的选择是这个interface IState<T, E, R extends IState<?, ?, ?>>。问号可以替换为any我们并不关心之后的状态是什么,只是它是某个状态。

interface IState<T, E, R extends IState<any, any, any>> {
    send(message: T, callback?: (event: E) => void): R;
}

class Left implements IState<'go-on', never, Left>, IState<'turn-right', never, Right>{
    send(m: 'go-on', cb?: (e: never) => void): Left;
    send(m: 'turn-right', cb?: (e: never) => void): Right;
    send(m: 'go-on' | 'turn-right', cb?: any) {
        return m === 'go-on' ? new Left() : new Right();
    }
}

class Right implements IState<'go-on', never, Right>, IState<'turn-left', never, Left> {
    send(m: 'go-on', cb?: (e: never) => void): Right;
    send(m: 'turn-left', cb?: (e: never) => void): Left;
    send(m: 'go-on' | 'turn-left', cb?: any) {
        return m === 'go-on' ? new Right() : new Left();
    }
}

let left = new Left();
let left_go_on: Left = left.send("go-on")
let left_turn_right: Right = left.send("turn-right")
left.send("turn-left") // error


let right = new Right();
let right_go_on: Right = right.send("go-on")
let right_turn_right: Left = right.send("turn-left")
right.send("turn-right") // error

或者,如果您只想在implements子句中使用接口,这也可以:

interface IState<T extends [any, any, IState<[any, any, any]>]> {
    send: T extends T  ? ((message: T[0], callback?: (event: T[1]) => void) => T[2]) : never
}

class Left implements IState<['go-on', never, Left] | ['turn-right', never, Right]>{
    send(m: 'go-on', cb?: (e: never) => void): Left;
    send(m: 'turn-right', cb?: (e: never) => void): Right;
    send(m: 'go-on' | 'turn-right', cb?: any) {
        return m === 'go-on' ? new Left() : new Right();
    }
}

class Right implements IState<['go-on', never, Right] | ['turn-left', never, Left]> {
    send(m: 'go-on', cb?: (e: never) => void): Right;
    send(m: 'turn-left', cb?: (e: never) => void): Left;
    send(m: 'go-on' | 'turn-left', cb?: any) {
        return m === 'go-on' ? new Right() : new Left();
    }
}

let left = new Left();
let left_go_on: Left = left.send("go-on")
let left_turn_right: Right = left.send("turn-right")
left.send("turn-left") // error


let right = new Right();
let right_go_on: Right = right.send("go-on")
let right_turn_right: Left = right.send("turn-left")
right.send("turn-right") // error
于 2019-06-21T10:15:10.963 回答