Skip to content

Unable to narrow the type of a tagged union when using a match statement #15190

Closed

Description

@jonathan-laurent

Bug Report

Mypy is unable to narrow the type of a tagged union when deconstructing it using a match statement.

To Reproduce

from typing import Literal, TypeAlias


Expr: TypeAlias = (
    tuple[Literal["const"], int] | tuple[Literal["add"], tuple["Expr", "Expr"]]
)


# OK
def eval(e: Expr) -> int:
    if e[0] == "const":
        return e[1]
    else:
        e1, e2 = e[1]
        return eval(e1) + eval(e2)


# Rejected by mypy (see errors below)
def eval2(e: Expr) -> int:
    match e:
        case ("const", x):
            return x
        case ("add", (e1, e2)):
            return eval2(e1) + eval2(e2)

Expected Behavior

Both eval and eval2 should typecheck.

Actual Behavior

Function eval typechecks but eval2 does not.

Mypy playground link

main.py:19: error: Missing return statement  [return]
main.py:22: error: Incompatible return value type (got "object", expected "int")  [return-value]
main.py:24: error: Argument 1 to "eval2" has incompatible type "object"; expected "Expr"  [arg-type]
Found 3 errors in 1 file (checked 1 source file)

Your Environment

  • Mypy version used: 1.2.0
  • Mypy command-line flags: none
  • Mypy configuration options from mypy.ini (and other config files): none
  • Python version used: 3.11

Activity

  1. tmke8 commented on May 6, 2023

    @tmke8
    Contributor

    Related: #12364

  2. hwong557 commented on May 6, 2023

    @hwong557

    Fundamentally the same, but here is a slightly simpler example:

    from typing import Literal, TypeAlias
    
    T: TypeAlias = tuple[Literal["str"], str] | tuple[Literal["float"], float]
    
    
    def f() -> T:
        ...
    
    
    pair: T = f()
    match pair[0]:
        case "str":
            reveal_type(pair)
            # mypy should know pair[1] is a str.
            reveal_type(pair[1])
        case "float":
            reveal_type(pair)
            # mypy should know pair[1] is a float.
            reveal_type(pair[1])
    

    Gist URL: https://gist.github.com/mypy-play/86dc7bd0856c13501dc21aa34ddb9b30
    Playground URL: https://mypy-play.net/?mypy=master&python=3.11&gist=86dc7bd0856c13501dc21aa34ddb9b30

  3. added a commit that references this issue on Feb 11, 2026
    9752e19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions