Skip to content

__eq__ methods disable narrowing in the else block #17229

Open
@DetachHead

Description

@DetachHead
from typing import Literal, override


class Foo:
    @override
    def __eq__(self, value: object, /) -> bool:
        return True


def fun(a: Foo | Literal["a"]) -> None:
    if a == "a":
        reveal_type(a) # Foo | Literal['a']
    else:
        reveal_type(a) # Foo | Literal['a']

playground

in this case, it's safe to narrow to Foo in the else block, because the Literal["a"] cannot have a custom __eq__. this means if a == "a" returns False, there's no chance that a is a Literal["a"], but if it returns True then it's still possible for it to be a Foo because of the Foo.__eq__.

therefore, the narrowing should work like this:

def fun(a: Foo | Literal["a"]) -> None:
    if a == "a":
        reveal_type(a) # Foo | Literal['a']
    else:
        reveal_type(a) # Foo

this would also be consistent with pyright's behavior: playground

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugmypy got something wrongtopic-type-narrowingConditional type narrowing / binder

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions