Skip to content

Mypy infers strange types for unions of T | Wrapper[T] #21615

@fabiommendes

Description

@fabiommendes

Consider the ambiguous (yet somewhat useful) type: T | list[T]. It is problematic since given any list, e.g., list[int], it can bind T to int, which is the expected behavior, or to list[int], which is surprising, but yet consistent with the type definition. Mypy does neither.

from typing import assert_type, Never

def f[T](x: T | list[T]) -> list[T] | T:
    if isinstance(x, list):
        return [y for y in x]
    else:
        return x

reveal_type(f(1)) # list[int] | int
reveal_type(f([])) # list[Never]

reveal_type(f([1, 2, 3])) # list[Never]
# It also flags an issue: expect that argument for f to be list[Never]

The first revealed type is correct.

The second one (for f([])), is strange. It probably should infer Any | list[Any] (which simplifies to Any). Mypy assigns list[Never] which perhaps is acceptable if we remember that list[Never] | Never also simplifies to list[Never].

However, Mypy infers T = Never regardless of the list being empty or not and the issue persists if we replace list with any generic container type of T. The last example illustrate this and Mypy complaints that [1,2,3] is not of the type list[Never].

I checked with Pyright, and it does (mostly) the expected behavior. It fails,however when trying to infer the type of an empty list, but inference on empty lists is always problematic, so I think it is acceptable.

  • Mypy version used: 2.1 (but i tested with many others in Mypy playground)
  • Python version used: 3.13

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrong
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions