Annotating function with TypeVar and default value results in union type

Question:

When annotating a function parameter with a bound TypeVar, giving it a default value results in the parameter having a union type between the TypeVar and the default value type, even though the default value is of the TypeVar type.

Example:

class A:
    pass

class B(A):
    pass

Instance = TypeVar("Instance", bound=A)

def get_instance(cls: type[Instance] = A) -> Instance:
    return cls()

Running mypy yields the following error: error: Incompatible default for argument "cls" (default has type "Type[A]", argument has type "Type[Instance]").

reveal_type is correct in both cases:

instance_a = get_instance(cls=A)
reveal_type(instance_a)  # note: Revealed type is "A"

instance_b = get_instance(cls=B)
reveal_type(instance_b)  # note: Revealed type is "B"

How do I correctly annotate get_instance so that I can keep the default argument?

Asked By: connesy

||

Answers:

It is a very old mypy issue.

The general solution is using overload‘s, it can be applied in your case:

from typing import TypeVar, overload

class A:
    pass

class B(A):
    pass

_Instance = TypeVar("_Instance", bound=A)

@overload
def get_instance() -> A: ...
@overload
def get_instance(cls: type[_Instance]) -> _Instance: ...
def get_instance(cls: type[A] = A) -> A:
    return cls()

instance_a = get_instance(cls=A)
reveal_type(instance_a)  # note: Revealed type is "A"

instance_b = get_instance(cls=B)
reveal_type(instance_b)  # note: Revealed type is "B"

For type checking purposes, we define two overloads. Implementation signature is checked only within the function, external callers see only the overloads. Here we just ignore specific relationship between cls type and return type while checking body, however, you can consider other solutions from the linked issue.

Try me in playground

Answered By: SUTerliakov
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.