[Python] Negative narrowing of union type with TypeIs of TypeVar not working as expected in...

Discussão em 'Python' iniciado por Stack, Novembro 27, 2025.

  1. Stack

    Stack Membro Participativo

    I recently upgraded mypy from 1.17.0 to 1.18.2 The following code was successfully validated in the old mypy version (1.17.0), but fails in the new one (1.18.2):

    _T = TypeVar('_T')


    class Foo(Generic[_T], ABC):
    pass


    def to_foo(value: _T | Foo[_T],
    checker: Callable[[Any], TypeIs[_T]],
    factory: Callable[[_T], Foo[_T]]) -> Foo[_T]:
    if checker(value):
    return factory(value)
    else:
    return value


    This is the error:

    error: Incompatible return value type (got "_T | Foo[_T]", expected "Foo[_T]") [return-value]


    mypy seems unhappy with the second return statement. It seems, it can not deduce, that if value is _T | Foo[_T] to begin with, and is not _T (the else case), that is has to be of type Foo[_T].

    Although, I thought that is exactly what TypeIs does:


    Type narrowing is applied in both the positive and negative case
    ​

    When I reduce the problem further, I end up with the following function which still fails:

    _T = TypeVar('_T')


    def mypy_fails(x: _T | int, checker: Callable[[Any], TypeIs[_T]], default: int) -> int:
    if checker(x):
    return default
    else:
    return x


    It seems to be related to the TypeVar, since the following function (with int and _T switched) still validates:

    def mypy_validates(x: int | _T, checker: Callable[[Any], TypeIs[int]], default: _T) -> _T:
    if checker(x):
    return default
    else:
    return x


    What is wrong with my initial code? Why does mypy_validates work? And why was it fine in the old mypy version, but not the newer one?

    Continue reading...

Compartilhe esta Página