How do you emulate ADTs and pattern matching in TypeScript?

前端 未结 6 1395
清酒与你
清酒与你 2020-12-20 13:53

Unfortunately, as of 0.9.5, TypeScript doesn\'t (yet) have algebraic data types (union types) and pattern matching (to destructure them). What\'s more, it doesn\'t even supp

6条回答
  •  情话喂你
    2020-12-20 14:21

    This is an old question, but maybe this will still help someone:

    Like @SorenDebois's answer, this one has half of the per-case boilerplate as @theSoft's. It is also more encapsulated than @Soren's. Additionally, this solution has type safety, switch-like behavior, and forces you to check all cases.

    // If you want to be able to not check all cases, you can wrap this type in `Partial<...>`
    type MapToFuncs = { [K in keyof T]: (v: T[K]) => void }
    // This is used to extract the enum value type associated with an enum. 
    type ValueOfEnum<_T extends Enum, U = any> = EnumValue
    
    class EnumValue {
      constructor(
        private readonly type: keyof T,
        private readonly value?: T[keyof T]
      ) {}
    
      switch(then: MapToFuncs) {
        const f = then[this.type] as (v: T[keyof T]) => void
        f(this.value)
      }
    }
    
    // tslint:disable-next-line: max-classes-per-file
    class Enum {
      case(k: K, v: T[K]) {
        return new EnumValue(k, v)
      }
    }
    

    Usage:

    // Define the enum. We only need to mention the cases once!
    const GameState = new Enum<{
      NotStarted: {}
      InProgress: { round: number }
      Ended: {}
    }>()
    
    // Some function that checks the game state:
    const doSomethingWithState = (state: ValueOfEnum) => {
        state.switch({
          Ended: () => { /* One thing */ },
          InProgress: ({ round }) => { /* Two thing with round */ },
          NotStarted: () => { /* Three thing */ },
        })
    }
    
    // Calling the function
    doSomethingWithState(GameState.case("Ended", {}))
    

    The one aspect here that is really not ideal is the need for ValueOfEnum. In my application, that was enough for me to go with @theSoft's answer. If anyone knows how to compress this, drop a comment below!

提交回复
热议问题