Skip to content

Mypy doesn't recognize that different string literals in if/else statement match a union of string literals  #18442

@notatallshaw

Description

@notatallshaw

Bug Report

When a function has a type signature like def foo(bar: Literal["a", "b"]) -> None: ... mypy throws an error if the argument passed in can be either a or b as it implies a general string.

To Reproduce

from typing import Literal

CHOICE = bool(...)

def foo(bar: Literal["a", "b"]) -> None: ...

def main() -> None:
    bar = "a"
    if CHOICE:
        bar = "b"

    foo(bar)

Expected Behavior

It would be nice if mypy recognized that bar here could only be Literal["a"] or Literal["b"], i.e Literal["a", "b"].

Actual Behavior

mre.py:12: error: Argument 1 to "foo" has incompatible type "str"; expected "Literal['a', 'b']" [arg-type]

Your Environment

  • Mypy version used: 1.14.1
  • Mypy command-line flags: Default
  • Mypy configuration options from mypy.ini (and other config files): Default
  • Python version used: 3.13

Context

This came up because of a recent change in typeshed python/typeshed#12181 which causes this pip code to trigger a mypy error: https://github.com/pypa/pip/blob/24.3.1/src/pip/_internal/utils/unpacking.py#L179

I assume this issue of different implicit Literal / str types inside branches has been discussed before but I couldn't find it searching through the GitHub issues.

x-ref: pypa/pip#13148

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrongtopic-join-v-unionUsing join vs. using unions

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions