Skip to content

Type not narrowed correctly for if X in collection #3229

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
frexvahi opened this issue Apr 24, 2017 · 6 comments · Fixed by #17344
Open

Type not narrowed correctly for if X in collection #3229

frexvahi opened this issue Apr 24, 2017 · 6 comments · Fixed by #17344
Labels
feature priority-1-normal topic-type-narrowing Conditional type narrowing / binder

Comments

@frexvahi
Copy link

def foobar(foo: Union[str, float]):
    if foo in ['fizz', 'buzz']:
        reveal_type(foo)
    else:
        pass

The revealed type is Union[builtins.str, builtins.float] but should be builtins.str

@ilevkivskyi
Copy link
Member

Than you for reporting this! I have also encountered situations where this could be useful. It is not super easy to implement, but I think this is a reasonable feature to support (especially for invariant collections, where the type is known precisely).

As a workaround, you could add assert isinstance(foo, str) in the if-branch, mypy will recognize this.

@elazarg
Copy link
Contributor

elazarg commented Apr 25, 2017

Related #2357 #1203

@frexvahi
Copy link
Author

frexvahi commented Aug 28, 2019

I just realised: there might also have to be a special case for testing if a float is in a set of ints, or any other situation where a == b can be true even though type(a) == type(b) is not.

@AlexWaygood AlexWaygood added the topic-type-narrowing Conditional type narrowing / binder label Aug 29, 2022
Jordandev678 added a commit to Jordandev678/mypy that referenced this issue Jun 7, 2024
hauntsaninja pushed a commit that referenced this issue Jul 24, 2024
Enables the narrowing of variable types when checking a variable is "in"
a collection, and the collection type is a subtype of the variable type.

Fixes #3229 

This PR updates the type narrowing for the "in" operator and allows it
to narrow the type of a variable to the type of the collection's items -
if the collection item type is a subtype of the variable (as defined by
is_subtype).

Examples
```python
def foobar(foo: Union[str, float]):
    if foo in ['a', 'b']:
        reveal_type(foo)  # N: Revealed type is "builtins.str"
    else:
        reveal_type(foo)  # N: Revealed type is "Union[builtins.str, builtins.float]"
```
```python
typ: List[Literal['a', 'b']] = ['a', 'b']
x: str = "hi!"
if x in typ:
    reveal_type(x)  # N: Revealed type is "Union[Literal['a'], Literal['b']]"
else:
    reveal_type(x)  # N: Revealed type is "builtins.str"
```

One existing test was updated, which compared `Optional[A]` with "in" to
`(None,)`. Piror to this change that resulted in `Union[__main__.A,
None]`, which now narrows to `None`. Test cases have been added for
"in", "not in", Sets, Lists, and Tuples.

I did add to the existing narrowing.pyi fixture for the test cases. A
search of the *.test files shows it was only used in the narrowing
tests, so there shouldn't be any speed impact in other areas.

---------

Co-authored-by: Jordandev678 <20153053+Jordandev678@users.noreply.github.com>
@JukkaL JukkaL reopened this Oct 2, 2024
@JukkaL
Copy link
Collaborator

JukkaL commented Oct 2, 2024

Reopening, since the original implementation was reverted due to some issues (#17864).

@Jordandev678
Copy link
Contributor

@JukkaL want me to pick this up?

@JukkaL
Copy link
Collaborator

JukkaL commented Oct 10, 2024

@Jordandev678 Sure! The original PR can be resubmitted once the issues have been addressed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature priority-1-normal topic-type-narrowing Conditional type narrowing / binder
Projects
None yet
6 participants