mypy icon indicating copy to clipboard operation
mypy copied to clipboard

Having a function that takes `Optional[T]` as an input and returns `T` (A type var) works incorrectly if T is an union

Open superosku opened this issue 1 year ago • 1 comments

Bug Report

Having a function that takes Optional[T] as an input and returns T (A type var) works incorrectly if T is an union.

To Reproduce

from typing import TypeVar


class A:
    pass


class B:
    pass


T = TypeVar("T")


def make_non_optional(value: T | None) -> T:
    if value is None:
        raise ValueError("Value is None")
    return value


def make_non_optimal_simple_example() -> A:
    my_value: A | None = None
    return make_non_optional(my_value)  # This works as expected


def make_non_optimal_complex_example_broken() -> A | B:
    my_value: A | B | None = None
    return make_non_optional(my_value)  # error: Incompatible return value type (got "object", expected "A | B")  [return-value]

Expected Behavior

When calling make_non_optional(my_value) with A | B | None the returned type should be A | B

Actual Behavior

It has object type as the return type

Your Environment

Mypy version used:

 $ mypy --version
mypy 1.10.0 (compiled: yes)

Python version used:

 $ python --version
Python 3.12.2

Mypy command-line flags: none

Mypy configuration options from mypy.ini (and other config files): none

superosku avatar Jun 14 '24 09:06 superosku

This behavior stems from the fact that mypy's constraint solver uses a join operator rather than unions. The join of A and B is object, which explains the error message you're seeing here.

erictraut avatar Jun 14 '24 14:06 erictraut

Does anyone know of a good workaround for this (other than the obvious # type: ignore)?? I have a code-base where this is coming up repeatedly...

stephenskett avatar Nov 06 '24 10:11 stephenskett