Closed
Description
Type narrowing behaves inconsistently:
def f(x: str | int) -> None:
if x == "x":
reveal_type(x) # str | int
if x in ["x"]:
reveal_type(x) # str
Both of these should infer the same type. This is a regression that was introduced in #17344 (cc @Jordandev678).
Also, unlike with ==, narrowing using "in" doesn't consider the possibility of a custom __eq__
method, so it can infer the wrong type:
class C:
def __init__(self, x: int) -> None:
self.x = x
def __eq__(self, other: object) -> bool:
if isinstance(other, C) and other.x == self.x:
return True
return NotImplemented
class D(C):
pass
def f(x: C) -> None:
if x in [D(5)]:
print(type(x)) # C
reveal_type(x) # D
f(C(5))
I'm going to revert to #17344 since fixing the logic may be non-trivial, and there is also another related bug (#17841).
The original PR can be submitted again with a fix that uses the same logic we use for ==
narrowing. If there is desire to support more narrowing use cases, this should be addressed in a separate issue/PR, and both == and "in" narrowing should remain consistent.