How do you emulate ADTs and pattern matching in TypeScript?

前端 未结 6 1387
清酒与你
清酒与你 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:14

    Here's an alternative to the very good answer by @thSoft. On the plus side, this alternative

    1. has potential interoperability with raw javascript objects on the form { type : string } & T, where the shape of T depends on the value of type,
    2. has substantially less per-choice boilerplate;

    on the negative side

    1. does not enforce statically that you match all cases,
    2. does not distinguish between different ADTs.

    It looks like this:

    // One-time boilerplate, used by all cases. 
    
    interface Maybe { value : T }
    interface Matcher { (union : Union) : Maybe }
    
    interface Union { type : string }
    
    class Case {
      name : string;
      constructor(name: string) {
        this.name = name;
      }
      _ = (data: T) => ( ({ type : this.name, data : data }) )
      $ =
        (f:(t:T) => U) => (union : Union) =>
            union.type === this.name
              ? { value : f((union).data) }
              : null
    }
    
    function match(union : Union, destructors : Matcher [], t : T = null)
    {
      for (const destructor of destructors) {
        const option = destructor(union);
        if (option)
          return option.value;
      }
      return t;
    }
    
    function any(f:() => T) : Matcher {
      return x => ({ value : f() });
    }
    
    // Usage. Define cases.
    
    const A = new Case("A");
    const B = new Case("B");
    
    // Construct values.
    
    const a = A._(0);
    const b = B._("foo");
    
    // Destruct values.
    
    function f(union : Union) {
      match(union, [
        A.$(x => console.log(`A : ${x}`))
      , B.$(y => console.log(`B : ${y}`))
      , any (() => console.log(`default case`))
      ])
    }
    
    f(a);
    f(b);
    f({});
    

提交回复
热议问题