python / python/mypy

Union of `Never` can't be used

Open
#18,779 3 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

bug topic-union-types
Dominant language
Python
Stars
20.6k
Forks
3.3k
PR merge metrics
PR metrics pending

Description

Bug Report

Functions which optionally take a value of Never type have impossible requirements placed on usages of that optional value.

To Reproduce

https://gist.github.com/mypy-play/a49d72bdacc179ffda5f69d8b66f41ff

from typing import Never


def blah(s: Never | str) -> None:
    print(s + "hi")

Expected Behavior

The code type-checks. Since it is impossible for any variable to take a value of type Never, the type signature of blah is equivalent to str -> None. The function's body is valid when s is annotated with type str, so it should be valid when annotated with Never | str.

Actual Behavior

main.py:5: error: Unsupported left operand type for + ("Never")  [operator]
main.py:5: note: Left operand is of type "Never | str"
Found 1 error in 1 file (checked 1 source file)

Your Environment

  • Python 3.12
  • MyPy 1.15.0, invocation as performed in the Playground

Background

Pyright appears to do the right thing here: it accepts this code.

I ran into this when implementing this pattern, which is me placing types around something I found untyped in the wild:

T = TypeVar("T", contravariant=True)
class _MyProtocol(Protocol[T]):
  def _foo(self, arg: T | IO[str]) -> None: ...

try:
    import foo
    class _MyImpl(_MyProtocol[foo.Foo]):
      def _foo(self, arg: foo.Foo | IO[str]) -> None: 
        # do something here
        pass

    MyType = _MyImpl

except ImportError:
    class _MyImplNone(_MyProtocol[Never]):
      def _foo(self, arg: Never | IO[str]) -> None:
        # correctly assume arg is an IO[str] here
        pass

    MyType = _MyImplNone

That way, MyType has type _MyProtocol[foo.Foo] | _MyProtocol[Never], allowing a type-level discrimination of whether the foo functionality is present or not.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the minimal Python reproducer in the issue or linked mypy-play example and compare the reported operator error with the expected str-equivalent behavior. Trace how unions containing Never are handled during type checking, then add coverage for the shown function and confirm mypy accepts it without the Never operand error.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
devtools
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
42/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.