Skip to content

TypeVar incompatible with constrained union #9424

Closed
@NeilGirdhar

Description

@NeilGirdhar
from typing import Optional, TypeVar

U = TypeVar('U', bound=Optional[int])


def f(u: U) -> U:
    if u is None:
        return None
    assert isinstance(u, int)  # removing this line causes it to pass.
    return u

Gives

main.py:10: error: Incompatible return value type (got "int", expected "U")

I cannot remove the assertion since it stands in for other code that is effectively constraining u.

This can be expressed tediously using overload:

from typing import Optional, TypeVar, overload


@overload
def f(u: None) -> None: ...

@overload
def f(u: int) -> int: ...

def f(u: Optional[int]) -> Optional[int]:
    if u is None:
        return None
    assert isinstance(u, int)
    return u

Would it be possible to get the TypeVar approach to pass? It is far more succinct, and this quickly blows up in complexity if there are more argument-return type constraints to be enforced.

This pattern is analogous to the common class factory pattern:

class C: pass
class B(C): pass
class A(B): pass

T = TypeVar('T', bound=C)
def factory(cls: Type[T]) -> T:
    return cls()

Since A < B < C, this enforces

factory(Type[C]) -> C
factory(Type[B]) -> B
factory(Type[A]) -> A

This issue argues that since int < Optional[int] and None < Optional[int], we should ideally be able to similarly specify that

f(int) -> int
f(None) -> None

using the same notation.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrong

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions