【问题标题】:How to express mixed ad-hoc and parametric polymorphic in typescript?如何在 typescript 中表达混合的 ad-hoc 和参数多态?
【发布时间】:2019-06-21 05:10:57
【问题描述】:

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

我正在尝试对有限状态机的状态进行抽象,并提出以下定义(在打字稿中)

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

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

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


例如,我正在尝试制作一个只有两种状态的简单状态机,LEFTRIGHT,以及三个可能的消息 go-on 左转右转。下表显示了它们的关系。

关键是我想限制状态 LEFT 只接受 go-onturn-right 消息,同时发送 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

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

related GitHub issue


我能想到的另一个抽象是

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


这个问题定义明确吗?

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

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

【问题讨论】:

    标签: typescript generics overloading parametric-polymorphism adhoc-polymorphism


    【解决方案1】:

    我认为最好的选择是interface IState&lt;T, E, R extends IState&lt;?, ?, ?&gt;&gt;。问号可以替换为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 子句中使用 on 接口,这也可以:

    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
    

    【讨论】:

      猜你喜欢
      • 2020-12-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多