title: Genericizing a class using a TypeVar with a bound from another generic class
body: <p>If <code>Foo</code> is generic in <code>T</code>, <code>U</code> and <code>Bar</code> is generic in <code>V</code> but stores an instance of <code>Foo</code>, then to avoid writing</p>
  <pre><code>class Bar(Generic[T, U, V]): 
      def __init__(self, foo: Foo[T, U], v: V): 
          ...
  </code></pre>
  <p>we could define <code>FooT = TypeVar(&quot;FooT&quot;, Foo[Any, Any])</code> and write</p>
  <pre><code>class Bar(Generic[FooT, V]): 
      def __init__(self, foo: FooT, v: V): 
           pass
  </code></pre>
  <p>Then instantiating <code>Bar</code> becomes <code>Bar[Foo[int, str], bool]</code> for example instead of <code>Bar[int, str, bool]</code>.  To me this is an improvement in terms of code clarity and maintainability.  But even with a simpler toy example, I run into problems with this pattern:</p>
  <pre><code>from typing import Generic, TypeVar, Union, TYPE_CHECKING
  
  ResultT = TypeVar(&quot;ResultT&quot;)
  
  class Query(Generic[ResultT]):
      def __init__(self, r: ResultT) -&gt; None:
          self.r = r
  
  QueryT = TypeVar(&quot;QueryT&quot;, bound=Query[Any])
  
  class Client(Generic[QueryT]):
      def __init__(self, q: QueryT) -&gt; None:
          self.q = q
          self.r = q.r
  
  c = Client[Query[int]](Query[int](2))
  if TYPE_CHECKING:
      reveal_type(c.q)
      reveal_type(c.q.r)
      reveal_type(c.r)
  print(id(c.r) == id(c.q.r))  # true
  
  ----
  mypytest.py:17: note: Revealed type is &quot;mypytest.Query[builtins.int]&quot;
  mypytest.py:18: note: Revealed type is &quot;builtins.int&quot;
  mypytest.py:19: note: Revealed type is &quot;Any&quot;
  Success: no issues found in 1 source file
  </code></pre>
  <p>I am surprised mypy is inferring <code>Any</code> in this situation when <code>c.q.r</code> and <code>c.r</code> are identical.  Basically it is assuming <code>c.r</code> is the upper bound of <code>ResultT</code> which is <code>Any</code> in this case, instead of the type implied by instantiation of <code>Query[int]</code>.  But what bothers me is the type is correctly resolved depending on the access pattern...</p>
  <p>Some other interesting modifications (which probably speak to my relatively newness to working with Python's generics), based on if I change the <code>QueryT</code> definitions and run <code>mypy</code> with <code>--strict</code>:</p>
  <pre><code>QueryT = TypeVar(&quot;QueryT&quot;, bound=Query[Union[int, str]])
  
  ---
  mypytest.py:22: error: Value of type variable &quot;QueryT&quot; of &quot;Client&quot; cannot be &quot;Query[int]&quot;
  mypytest.py:24: note: Revealed type is &quot;mypytest.Query[builtins.int]&quot;
  mypytest.py:25: note: Revealed type is &quot;builtins.int&quot;
  mypytest.py:26: note: Revealed type is &quot;Union[builtins.int, builtins.str]&quot;
  </code></pre>
  <p>This is basically the same behavior as above, but now the upper bound is <code>Union[int, str]</code> as expected (but again, why is the type now narrowed to <code>int</code>?).  I also get an error in strict mode -- I thought this assignment would be allowed?</p>
  <pre><code>QueryT = TypeVar(&quot;QueryT&quot;, bound=Union[Query[int], Query[str]])
  
  ---
  mypytest.py:18: note: Revealed type is &quot;mypytest.Query[builtins.int]&quot;
  mypytest.py:19: note: Revealed type is &quot;builtins.int&quot;
  mypytest.py:20: note: Revealed type is &quot;Union[builtins.int, builtins.str]&quot;
  </code></pre>
  <p>The error is resolved by factoring out the <code>Union</code>.  I'm sure the explanation is fairly obvious, just need to think about it.</p>
  <pre><code>QueryT = TypeVar(&quot;QueryT&quot;, Query[int], Query[str])
  
  ---
  mypytest.py:24: error: Need type annotation for &quot;q&quot;
  mypytest.py:25: error: Need type annotation for &quot;r&quot;
  mypytest.py:29: note: Revealed type is &quot;Any&quot;
  mypytest.py:30: note: Revealed type is &quot;Any&quot;
  mypytest.py:31: note: Revealed type is &quot;Any&quot;
  </code></pre>
  <p>This simple case of restricting the TypeVar to two types seems the simplest to reason about, yet it gives the type checker the most problems.  Why does it need an explicit annotation here to avoid inferring <code>Any</code>?</p>
  <p>By just skipping the intermediate <code>QueryT</code> type, everything works w/ no errors under strict:</p>
  <pre><code>class Client(Generic[ResultT]):
      def __init__(self, q: Query[ResultT]) -&gt; None:
          ...
  c = Client[int](Query[int](2))
  ...
  
  ---
  mypytest.py:39: note: Revealed type is &quot;mypytest.Query[builtins.int]&quot;
  mypytest.py:40: note: Revealed type is &quot;builtins.int&quot;
  mypytest.py:41: note: Revealed type is &quot;builtins.int&quot;
  </code></pre>
  <p>Absent some insight into what's going on / advice on best practices, I will probably just collect and repeat all the primitive generic types in use for a class; mypy already forces you to do this for inheritance, so probably not a big deal for composition.</p>