Skip to content

Incorrect return type inference when generic function is passed non-union value #10481

@kurtbrose

Description

@kurtbrose

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 error

Expected 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    addressed in next versionIssue is fixed and will appear in next published versionbugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions