Search code examples
pythonpython-typingmypy

typing: TypeVar vs. Union, mypy error: Missing type parameters for generic type


I'm struggling to understand when to use TypeVar and when to use Union despite spending a significant amount of time in the typing documentation, the mypy documentation, and PEP 483.

Simple version of the question: what's the difference between Numeric = TypeVar('Numeric', int, float) and Numeric = Union[int, float]?

Here's a more detailed example of what I'm running into:

"""example1.py
Use of Union to define "Numeric" type.
"""

from typing import Union, Sequence

Numeric = Union[int, float]

Vector = Sequence[Numeric]

Matrix = Sequence[Vector]

Check with mypy:

$ mypy --strict example1.py
Success: no issues found in 1 source file 

Use TypeVar instead:

"""example2.py
Use of TypeVar to define "Numeric" type.
"""

from typing import TypeVar, Sequence

Numeric = TypeVar('Numeric', int, float)

Vector = Sequence[Numeric]

Matrix = Sequence[Vector]

Check with mypy:

$ mypy --strict example2.py
example2.py:11: error: Missing type parameters for generic type "Vector"
Found 1 error in 1 file (checked 1 source file)

The above mypy error is referring to the definition of Matrix. Why is mypy happy with example1.py but not example2.py?

I'm able to eliminate the error in example2.py by changing the last line to Matrix = Sequence[Vector[Numeric]].

Version information:

$ python --version
Python 3.8.4
$ mypy --version
mypy 0.782

Solution

  • TypeVar is for creating generic types.

    Numeric = TypeVar('Numeric', int, float)
    
    Vector = Sequence[Numeric]
    

    ... means Vector[T] is an alias for Sequence[T] with the constraint that issubclass(T, (int, float)). This means mypy considers Vector to be an incomplete type, it asks: "a Vector of what?", and you can write Vector[Numeric] to mean: "a Vector of any Numeric".

    It's often used for functions or classes, like this:

    T = TypeVar('T')
    V = TypeVar('V')
    def map(f: Callable[[T], V], it: Iterable[T]) -> Iterator[V]:
        for x in it:
            yield f(x)
    

    This means you can make things type-safe even if you don't know exactly what types you're gonna get.