Annotate a Python function so that it narrows down the return type of an union involving a typevar #1639
-
Hello, I'm currently in the process of adding type annotations for a medium-sized library. The library has some quite complex cases that I would like to handle. I managed to simplify these complex cases to the following, much easier, one. Consider the following pseudo-identity function: def g(x, y): return x, y Let's assume that this function accepts either two parameters of the same type (TL;DR: any pair of objects of the same type implementing the I'm looking for a way to annotate this function so that the return type can be deduced by mypy precisely. For instance, I'm expecting Naively, I wrote: from typing import TypeVar
T = TypeVar('T', bound=int)
def g(x: T | str, y: T | str) -> tuple[T | str, T | str]:
return x, y AFAIK, this should be enough to ensure that both parameters have the same type (and are instances of int) OR that one (or both) of them is/are str. However, when I execute mypy on this snippet as follows: reveal_type(g(3, 4))
reveal_type(g('hello', 'world'))
reveal_type(g('hello', 2)) I got this output:
You can see this live in mypy Playground: https://mypy-play.net/?mypy=latest&python=3.12&gist=a5063b88271ddd94a58c82e3376deb5c The second line is what I expected (a tuple of strings), but the two other calls should (or could) be refined to Is this something out of scope of the Notice that I cannot write To put some more context for this question (do not read if you don't care ;-), the library I want to annotate defines an Generalizing the above example, I currently have something like this: Comparable = ... # Protocol with __eq__, __lt__, __le__, ...
T = TypeVar('T', bound=Comparable)
Bound: TypeAlias = T | _PInf | _NInf
class Interval(Generic[T]):
def __init__(self, lower: Bound[T], upper: Bound[T]) -> None:
... The goal is (1) to make sure that both bounds are "compatible" (as explained in previous paragraph), and (2) that, for example, |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 1 reply
-
For polymorphic functions like this, Code sample in pyright playground from typing import TypeVar, overload
T = TypeVar("T", bound=int)
@overload
def g(x: T, y: T) -> tuple[T, T]: ...
@overload
def g(x: T, y: str) -> tuple[T, str]: ...
@overload
def g(x: str, y: T) -> tuple[str, T]: ...
@overload
def g(x: str, y: str) -> tuple[str, str]: ...
def g(x: T | str, y: T | str) -> tuple[T | str, T | str]:
return x, y |
Beta Was this translation helpful? Give feedback.
-
Thanks for your answer! I'll try this approach in the context of my generic |
Beta Was this translation helpful? Give feedback.
For polymorphic functions like this,
typing.overload
is usually the best approach.Code sample in pyright playground