-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Description
Describe the bug
When calling a generic function with multiple type parameters, Pyright fails to infer the correct specialized return type.
This causes incorrect errors during assignment, even though the overload resolution is unambiguous and should succeed.
In the example below, the function takes a value of type T1 | T2 and returns a tuple of (T1 | None, T2 | None) depending on which type matched.
However, calling which_type(1, int, str) causes Pyright to infer the return type as tuple[int | None, str | int | None] instead of tuple[int | None, str | None].
This only happens when the input value is not explicitly typed as int | str.
Code or Screenshots
import typing
def which_type[T1, T2](val: T1 | T2, t1: type[T1], t2: type[T2]) -> tuple[T1 | None, T2 | None]:
if isinstance(val, t1):
return (val, None)
if isinstance(val, t2):
return (None, val)
raise typing.Never
a: int | None
b: str | None
a, b = which_type(1, int, str) # ❌ Unexpected Pyright error:
# Type "str | int | None" is not assignable to type "str | None"
# Type "int" is not assignable to type "str"
def f(v: int | str):
a: int | None
b: str | None
a, b = which_type(v, int, str) # ✅ This works fine
def g(v: int):
a: int | None
b: str | None
a, b = which_type(v, int, str) # ❌ Unexpected Pyright errorExpected behavior:
Both calls should succeed and return a tuple of the correct (int | None, str | None) shape.
Here's a reproduction on the playground, you can see reveal_type() showing the confused type when passed 1, or an int typed parameter:
https://pyright-play.net/?code=JYWwDg9gTgLgBDAnmYA7A5gKEwEwKYBmcA7gBbADGpA%2BkmHgNoAqAjADRxMBMAugBQA3AIYAbAFycWcAD6cuHGCwl1GrHgq7Lkq3gEo4AWgB8CAK5gRqqbIByEVHg7cZcOw55jMcb3GBFgAM5oATBCqBR4gqIKLLqePglwUHgwplCocFEiHG54ul4%2Bfr5BqCFhEVkacQWJSSlpGXy5HMIi%2BQlQQoF4CMho6AB0NngCeFDYmEISaPC29niYAEYSIVAuudhCHItwALwk5FS02nzsvqgwHKv6cADEcIAy5HAACohQwOik8GNQ0HCkYwWyVGomO9D4ZEoNBUpw4MyuMCgunyuEIcAIgmmFxc13iPim51mrnmNTgyzgq3WJISWzJewOULBkQEcIuCKRpOBeFBMMhRxhLMJ7ORE3wRHQmMJ1RpWKJGwS5MpcwcpNpO32fOhJ0F8IpiPaPi5PJOmqZglZlz1SPyQA