title: How to encapsulate a join or flatten method in linked list?
body: <p>I have a basic linked list build in typescript with a discriminated union. </p>
  
  <pre><code>type ListType&lt;T&gt; = {
  Kind: "Cons",
  Head: T,
  Tail: List&lt;T&gt;
  } | {
   Kind: "Empty"
  }
  
  type ListOperations&lt;T&gt; = {
   reduce: &lt;U&gt;(this: List&lt;T&gt;, f: (state: U, x: T) =&gt; U, accumulator: U) =&gt; U
   map: &lt;U&gt;(this: List&lt;T&gt;, f: (_: T) =&gt; U) =&gt; List&lt;U&gt;
   reverse: (this: List&lt;T&gt;) =&gt; List&lt;T&gt;
   concat: (this: List&lt;T&gt;, l: List&lt;T&gt;) =&gt; List&lt;T&gt;
   toArray: (this: List&lt;T&gt;) =&gt; T[]
   join: (this: List&lt;List&lt;T&gt;&gt;) =&gt; List&lt;T&gt;
  }
  
  type List&lt;T&gt; = ListType&lt;T&gt; &amp; ListOperations&lt;T&gt;
  </code></pre>
  
  <p>I also have some constructors for both empty and cons: </p>
  
  <pre><code>export const Cons = &lt;T&gt;(head: T, tail: List&lt;T&gt;): List&lt;T&gt; =&gt; ({
   Kind: "Cons",
   Head: head,
   Tail: tail,
   ...ListOperations()
  })
  
  export const Empty = &lt;T&gt;(): List&lt;T&gt; =&gt; ({
     Kind: "Empty",
     ...ListOperations()
  })
  </code></pre>
  
  <p>And finally I have an implementation of the different methods: </p>
  
  <pre><code>const ListOperations = &lt;T&gt;(): ListOperations&lt;T&gt; =&gt; ({
  reduce: function &lt;U&gt;(this: List&lt;T&gt;, f: (state: U, x: T) =&gt; U, accumulator: U): U {
      return this.Kind == "Empty" ? accumulator : this.Tail.reduce(f, f(accumulator, this.Head))
  },
  map: function &lt;U&gt;(this: List&lt;T&gt;, f: (_: T) =&gt; U): List&lt;U&gt; {
      return this.reduce((s, x) =&gt; Cons(f(x), s), Empty())
  },
  reverse: function (this: List&lt;T&gt;): List&lt;T&gt; {
      return this.reduce((s, x) =&gt; Cons(x, s), Empty())
  },
  concat: function (this: List&lt;T&gt;, l: List&lt;T&gt;): List&lt;T&gt; {
      return this.reverse().reduce((s, x) =&gt; Cons(x, s), l)
  },
  toArray: function (this: List&lt;T&gt;): T[] {
      return this.reduce&lt;T[]&gt;((s, x) =&gt; s.concat([x]), [])
  },
  join: function (this: List&lt;List&lt;T&gt;&gt;): List&lt;T&gt; {
      return this.reduce((s, x) =&gt; s.concat(x), Empty())
  }
  
  })
  </code></pre>
  
  <p>Everything works fine, but I get a compile error when I try to run the following: </p>
  
  <pre><code>let x = Cons(1, Cons(2, Cons(3, Cons(4, Empty()))))
  let y = x.map(x =&gt; x + 4)
  
  let z = Cons(x, Cons(y, Empty()))
  z.join()
  </code></pre>
  
  <blockquote>
    <p>The 'this' context of type <code>List&lt;List&lt;number&gt;&gt;</code> is not assignable to 
      method's 'this' of type <code>List&lt;List&lt;List&lt;number&gt;&gt;&gt;</code>.</p>
  </blockquote>
  
  <p>This is because of the <code>join</code> method (or <code>flatten</code> as some of you might call it). When I write the join outside the List type then it works, so my question is: <strong>Is there a way to explicitly tell the compiler that <code>this</code> needs to be of type <code>List&lt;List&lt;T&gt;&gt;</code>?</strong> </p>
  
  <p>I already tried using <code>extends</code></p>
  
  <p><code>join: function &lt;T1 extends List&lt;T&gt;&gt;(this: List&lt;T1&gt;): List&lt;T&gt;</code></p>