Skip to content

Type narrowing for tuple worse with match than with the equivalent instanceof check #19082

Open
@mscheifer

Description

@mscheifer

Bug Report

Type narrowing is worse in a match statement on a tuple than the equivalent isinstance check.

With isinstance, mypy is able to determine that the type of a must be the tuple side of the union even though the check is only against Sequence, and then correctly narrows the type of the second element. With match, mypy is unable to do so and gives a union of the two element types in the tuple.

To Reproduce

repro.py

from collections.abc import Sequence

Type = tuple[float, int] | None


def foo(a: int) -> None:
    pass


def bar_match(a: Type) -> None:
    match a:
        case _, int_a:
            foo(int_a)


def bar_isinstance(a: Type) -> None:
    if isinstance(a, Sequence):
        _, int_a = a
        foo(int_a)

Expected Behavior

No errors

Actual Behavior

$ mypy repro.py 
repro.py:13: error: Argument 1 to "foo" has incompatible type "float | int"; expected "int"  [arg-type]
Found 1 error in 1 file (checked 1 source file)

Note that there is only one error, for the bar_match function, and no error for the bar_isinstance function.

Your Environment

I found this with 1.15 and reproduced it on the current master branch.

  • Mypy version used: 1.15 or 1.16.0+dev.ca609acabdc94ee973a53d62b8dcb7e55c789aec (compiled: no)
  • Mypy command-line flags: None
  • Mypy configuration options from mypy.ini (and other config files): None
  • Python version used: Python 3.12.3

Possibly related issues:
#19081

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrongtopic-match-statementPython 3.10's match statementtopic-type-narrowingConditional type narrowing / binder

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions