title: Python 3 Type Hints with Forward Reference and Proper Inheritance
body: <p>Working with Python 3.6 under Win10x64 in PyCharm 2017.</p>
  
  <p>I know typing (or even type hinting) is kind of unpopular in the Python community, but please bear with me.</p>
  
  <p>Let's say I have a <code>Node</code> class to create tree-like containers with arbitrary contents. Here is a highly simplified version:</p>
  
  <pre><code>from typing import Any, List
  
  class Node:
  
      def __init__(self, content: Any, parent: 'Node' = None):
          self.content = content  # type: Any
          self.parent = parent  # type: 'Node'
          self.children = []  # type: List['Node']
          if parent:
              parent.children.append(self)
  
      @property
      def descendants(self) -&gt; List['Node']:
          """Returns a list of nodes that are descendant from the node starting with its first child, then its
              descendants (and ending with the last descendant of its last child)."""
          out = []
          for ch in self.children:
              out.append(ch)
              for desc in ch.descendants:
                  out.append(desc)
          return out
  </code></pre>
  
  <p>I am trying to use proper type hints. Seeing as I need to indicate the type of <code>parent</code> to be <code>Node</code>, I used the quotes as prescribed in <a href="https://www.python.org/dev/peps/pep-0484/#forward-references" rel="nofollow noreferrer">PEP 484</a> for forward references. Same with <code>self.children</code>, using <code>List['Node']</code>, and also with the property <code>descendants</code>.</p>
  
  <p>If I inherit from Node (like so <code>class SpecialNode(Node): pass</code>), I would expect PyCharm's type checker to be able to infer all the relevant types correctly, such that <code>SpecialNode</code> is expected in place of <code>Node</code>.</p>
  
  <p>Consider this:</p>
  
  <pre><code>n_root = Node('foo')
  n_0 = Node(10, parent=n_root)
  n_01 = Node('bar', parent=n_0)
  n_root_and_children = [n_root] + n_root.children
  n_root_and_descendants = [n_root] + n_root.descendants
  
  s_root = SpecialNode('special_foo')
  s_0 = Node(-10, parent=s_root)
  s_01 = Node('special_bar', parent=s_0)
  s_root_and_children = [s_root] + s_root.children
  s_root_and_descendants = [s_root] + s_root.descendants
  </code></pre>
  
  <p>Obviously all of this still "works" at runtime, but PyCharm underlines <code>s_root.descendants</code> in the last line and tells me:</p>
  
  <p><strong>Expected type 'List[SpecialNode]' (matched generic type 'List[TypeVar('_T')]'), got 'List[Node]' instead</strong></p>
  
  <p>What am I doing wrong?</p>
  
  <p>.</p>
  
  <p><strong>EDIT:</strong></p>
  
  <p>Inspired by an answer that is now deleted, I figured out the solution using <code>TypeVar</code>, but do not fully understand, why it works. I would appreciate, if anyone could answer this better.</p>
  
  <pre><code>from typing import Any, List, TypeVar
  
  NodeType = TypeVar('Node')
  
  
  class Node:
  
      def __init__(self, content: Any, parent: NodeType = None):
          self.content = content  # type: Any
          self.parent = parent  # type: NodeType
          self.children = []  # type: List[NodeType]
          if parent:
              parent.children.append(self)
  
      @property
      def descendants(self) -&gt; List[NodeType]:
          """Returns a list of nodes that are descendant from the node starting with its first child, then its
              descendants (and ending with the last descendant of its last child)."""
          out = []
          for ch in self.children:
              out.append(ch)
              for desc in ch.descendants:
                  out.append(desc)
          return out
  
  
  class SpecialNode(Node):
      pass
  
  
  n_root = Node('foo')
  n_0 = Node(10, parent=n_root)
  n_01 = Node('bar', parent=n_0)
  n_root_and_children = [n_root] + n_root.children
  n_root_and_descendants = [n_root] + n_root.descendants
  
  s_root = SpecialNode('special_foo')
  s_0 = Node(-10, parent=s_root)
  s_01 = Node('special_bar', parent=s_0)
  s_root_and_children = [s_root] + s_root.children
  s_root_and_descendants = [s_root] + s_root.descendants
  </code></pre>
  
  <p>PyCharm leaves me alone and the code works properly.</p>