-
Sorry for the vague and maybe misleading title. I couldn't think of a good way to succinctly describe my problem. from __future__ import annotations
from typing import Any
from typing import overload
from typing import reveal_type
class C[T: tuple[Any, ...]]:
@overload
def foo(self: C[tuple[()]]) -> tuple[Any]: ...
@overload
def foo(self: C[tuple[Any]]) -> tuple[Any, Any]: ...
@overload
def foo(self: C[Any]) -> tuple[Any, ...]: ...
instance: C[tuple[Any]] = C()
reveal_type(instance.foo())
# Type of "instance.foo()" is "tuple[Any, Any]" => correct (overload 2)
instance: C[tuple[Any, Any]] = C()
reveal_type(instance.foo())
# Type of "instance.foo()" is "tuple[Any, ...]" => correct (fall through case)
instance: C[Any] = C()
reveal_type(instance.foo())
# Type of "instance.foo()" is "tuple[Any]" => wrong (overload 1)
# Should match the fall-through case, but always matches the first overload. The perfect solution would be def foo(self) -> tuple[Any, *T]: ... but that is not allowed in this context. Is there any way i can correctly type hint this class, so that an unknown specialization will match the fall-through case? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
Overload resolution behavior isn't currently specified in the typing specification, so you will may see differences between Python type checkers — especially when I don't understand the full constraints of your use case, but a TypeVarTuple might be preferable to a TypeVar. Here's how that might look. Code sample in pyright playground from __future__ import annotations
from typing import Any, overload
class C[*Ts]:
@overload
def foo(self: C[()] | C[Any]) -> tuple[*Ts]:
...
@overload
def foo(self) -> tuple[Any, ...]:
...
instance1 = C[Any]()
reveal_type(instance1.foo()) # tuple[Any]
instance2 = C[Any, Any]()
reveal_type(instance2.foo()) # tuple[Any, ...]
instance3 = C()
reveal_type(instance3.foo()) # tuple[Uknown, ...] If you simply want to prepend an Code sample in pyright playground from __future__ import annotations
from typing import Any
class C[*Ts]:
def foo(self) -> tuple[Any, *tuple[*Ts]]:
...
instance1 = C[*tuple[()]]()
reveal_type(instance1.foo()) # tuple[Any]
instance2 = C[Any]()
reveal_type(instance2.foo()) # tuple[Any, Any]
instance3 = C[Any, Any]()
reveal_type(instance3.foo()) # tuple[Any, Any, Any]
instance4 = C()
reveal_type(instance4.foo()) # tuple[Any, *tuple[Uknown, ...]] |
Beta Was this translation helpful? Give feedback.
Overload resolution behavior isn't currently specified in the typing specification, so you will may see differences between Python type checkers — especially when
Any
is involved. In your code sample, the behaviors of pyright and mypy appear to match. For a description of pyright's overload resolution behavior, refer to this documentation.I don't understand the full constraints of your use case, but a TypeVarTuple might be preferable to a TypeVar. Here's how that might look.
Code sample in pyright playground