title: mypy error, overload with Union/Optional, "Overloaded function signatures 1 and 2 overlap with incompatible return types"
body: <p>So, let's start with an example. Suppose we have several types that can be combined together, let's say we are using <code>__add__</code> to implement this. Unfortunately, due to circumstances beyond our control, everything has to be "nullable", so we are forced to use <code>Optional</code> everywhere. </p>
  
  <pre><code>from typing import Optional, List, overload
  class Foo:
      value: int
      def __init__(self, value: int) -&gt; None:
          self.value = value
      def __add__(self, other: 'Foo') -&gt; 'Optional[Foo]':
          result = self.value - other.value
          if result &gt; 42:
              return None
          else:
              return Foo(result)
  
  class Bar:
      value: str
      def __init__(self, value: str) -&gt; None:
          self.value = value
      def __add__(self, other: 'Bar') -&gt; 'Optional[Bar]':
          if len(self.value) + len(other.value) &gt; 42:
              return None
          else:
              return Bar(self.value + other.value)
  
  class Baz:
      value: List[str]
      def __init__(self, value:List[str]) -&gt; None:
          self.value = value
      def __add__(self, other: 'Bar') -&gt; 'Optional[Baz]':
          if len(self.value) + 1 &gt; 42:
              return None
          else:
              return Baz([*self.value, other.value])
  
  
  @overload
  def Add(this: Optional[Foo], that: Optional[Foo]) -&gt; Optional[Foo]:
      ...
  @overload
  def Add(this: Optional[Bar], that: Optional[Bar]) -&gt; Optional[Bar]:
      ...
  @overload
  def Add(this: Optional[Baz], that: Optional[Bar]) -&gt; Optional[Baz]:
      ...
  def Add(this, that):
      if this is None or that is None:
          return None
      else:
          return this + that
  </code></pre>
  
  <p>We want utility function that does null-checking for us, but can generically handle "combinable" types. Most of the types can only be combined with themselves, and just to be more true to my actual use-case, let's say one type combines with the other. I would have hoped that the <code>overload</code> decorator could have helped here, however mypy complains:</p>
  
  <pre><code>mcve4.py:35: error: Overloaded function signatures 1 and 2 overlap with incompatible return types
  mcve4.py:35: error: Overloaded function signatures 1 and 3 overlap with incompatible return types
  mcve4.py:38: error: Overloaded function signatures 2 and 3 overlap with incompatible return types
  </code></pre>
  
  <p>Using mypy version: <code>mypy 0.641</code></p>
  
  <p>Note, if I remove <code>Optional</code> madness, mypy doesn't complain. I can even keep one of them as optional!:</p>
  
  <pre><code>from typing import List, overload
  class Foo:
      value: int
      def __init__(self, value: int) -&gt; None:
          self.value = value
      def __add__(self, other: 'Foo') -&gt; 'Foo':
          result = self.value - other.value
          return Foo(result)
  
  class Bar:
      value: str
      def __init__(self, value: str) -&gt; None:
          self.value = value
      def __add__(self, other: 'Bar') -&gt; 'Bar':
          return Bar(self.value + other.value)
  
  class Baz:
      value: List[str]
      def __init__(self, value:List[str]) -&gt; None:
          self.value = value
      def __add__(self, other: 'Bar') -&gt; 'Optional[Baz]':
          return Baz([*self.value, other.value])
  
  
  @overload
  def Add(this: Foo, that: Foo) -&gt; Foo:
      ...
  @overload
  def Add(this: Bar, that: Bar) -&gt; Bar:
      ...
  @overload
  def Add(this: Baz, that: Bar) -&gt; 'Optional[Baz]':
      ...
  def Add(this, that):
      if this is None or that is None:
          return None
      else:
          return this + that
  </code></pre>
  
  <p>This makes me suspect that the "overlap" is for NoneType, but feel like this should be resolvable, am I completely off base?</p>
  
  <h3>Edit</h3>
  
  <p>So, I'm really just flailing about here, but I suppose, when both argument are <code>None</code> this is definitely ambiguous, I would have hoped that the following would resolve it:</p>
  
  <pre><code>@overload
  def Add(this: None, that: None) -&gt; None:
      ...
  @overload
  def Add(this: Optional[Foo], that: Optional[Foo]) -&gt; Optional[Foo]:
      ...
  @overload
  def Add(this: Optional[Bar], that: Optional[Bar]) -&gt; Optional[Bar]:
      ...
  @overload
  def Add(this: Optional[Baz], that: Optional[Bar]) -&gt; Optional[Baz]:
      ...
  def Add(this, that):
      if this is None or that is None:
          return None
      else:
          return this + that
  </code></pre>
  
  <p>But I am still getting:</p>
  
  <pre><code>mcve4.py:37: error: Overloaded function signatures 2 and 3 overlap with incompatible return types
  mcve4.py:37: error: Overloaded function signatures 2 and 4 overlap with incompatible return types
  mcve4.py:40: error: Overloaded function signatures 3 and 4 overlap with incompatible return types
  </code></pre>
  
  <p>Edit2
  Going along the same garden-trail, I've tried the following:</p>
  
  <pre><code>@overload
  def Add(this: None, that: None) -&gt; None:
      ...
  @overload
  def Add(this: Foo, that: Optional[Foo]) -&gt; Optional[Foo]:
      ...
  @overload
  def Add(this: Optional[Foo], that: Foo) -&gt; Optional[Foo]:
      ...
  @overload
  def Add(this: Baz, that: Bar) -&gt; Optional[Baz]:
      ...
  @overload
  def Add(this: Baz, that: Optional[Bar]) -&gt; Optional[Baz]:
      ...
  @overload
  def Add(this: Optional[Baz], that: Bar) -&gt; Optional[Baz]: # 6
      ...
  @overload
  def Add(this: Bar, that: Optional[Bar]) -&gt; Optional[Bar]:
      ...
  @overload
  def Add(this: Optional[Bar], that: Bar) -&gt; Optional[Bar]: # 8
      ...
  
  def Add(this, that):
      if this is None or that is None:
          return None
      else:
          return this + that
  </code></pre>
  
  <p>I am now getting:</p>
  
  <pre><code>mcve4.py:49: error: Overloaded function signatures 6 and 8 overlap with incompatible return types
  </code></pre>
  
  <p>Which is starting to make sense to me, I think fundamentally what I am trying to do is unsafe/broken. I may have to just cut the gordian knot another way...</p>