我不确定是否要在标题中描述当前的问题。 我要问的是来自以下要求。
我正在尝试为有限状态机的状态做一个抽象,并提出以下定义(在打字稿中)
interface IState {
send<T, E>(message: T, callback?:(event: E)=>void): IState;
}
我要表达的是,有限状态机的状态应该能够接受消息并返回新状态,并带有可选的回调以在过渡期间处理事件。
将此接口实现为具体状态时,会出现问题。
例如,我正在尝试制作一个只有两个状态的简单状态机,左和右,并带有三个可能的消息,左转,右转。下表显示了它们之间的关系。
关键是我要限制状态向左,只接受继续和右转消息,而发送向左转到左移是一个编译错误。
我试图在打字稿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。
感谢该问题下的善意答复,我知道将重载函数分配给泛型函数是错误的。但是,如何在保持特定状态仅接受所需类型的消息的同时,表达状态的一般接口?
我可以提出的另一种抽象是
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;
}
我找到了maybe related issue in GitHub about generic value。
这个问题定义明确吗?
如果为true,则上述方法列表中是否有正确的解决方案?
如果为假,正确的解决方案是什么?
答案 0 :(得分:1)
我认为最好的选择就是这个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