Skip to content

Narrow types after 'in' operator. #4071

Closed

Description

@ilevkivskyi

Sometimes it is useful to narrow down a type after an in/not in comparison. For example:

class Option:
    ...
opt: Union[Option, str]
if opt in ('verbose', 'silent'):
    reveal_type(opt)  # we know this is 'str'
else:
    ...  # still a union here

presets: List[Option]
if opt not in presets:
    ...  # still a union
else:
    reveal_type(opt)  # we know this can be only 'Option'

This seems to be especially useful for --strict-optional, for example:

class Node:
    fullname: Optional[str]
n: Node

reverse_aliases: Dict[str, str]
if n.fullname in reverse_aliases:
    reveal_type(n.fullname)  # This should be 'str'

(related #2980)

A similar idea was discussed in #1749, but emitting an error on if 'a' in [1, 2, 3]: ... would require a larger change: emitting errors on unreachable code in binder.

Activity

  1. self-assigned this
    on Oct 8, 2017
  2. elazarg commented on Oct 8, 2017

    @elazarg
    Contributor

    I think this can also fall out of #4063 (just as a note; of course a dedicated PR is more practical)

  3. gvanrossum commented on Oct 8, 2017

    @gvanrossum
    Member
  4. ilevkivskyi commented on Oct 8, 2017

    @ilevkivskyi
    MemberAuthor

    @gvanrossum

    Careful. For unconstrained x, after x in (1, 2), x may still be a float. (Or a fraction or decimal?)

    OK, makes sense, we should not narrow down if we start from Any and should not use promotions (this is actually default in is_overlapping_types). I will update the PR now.

  5. ilevkivskyi commented on Oct 8, 2017

    @ilevkivskyi
    MemberAuthor

    @gvanrossum OK, I updated PR #4072
    Now it does not narrow type from Any and does not use promotions:

    x: Any
    if x in [1, 2, 3]:
        reveal_type(x)  # Revealed type is 'Any'
    y: float
    if y in [1, 2, 3]:
        reveal_type(x)  # Revealed type is 'builtins.float'

    It is however not easy to prohibit this:

    z: object
    if z in [1, 2, 3]:
        reveal_type(z)  # Revealed type is 'builtins.int'

    This is still formally speaking unsafe, but I think it is a reasonable price to pay.

  6. added a commit that references this issue on Oct 13, 2017
    b875205
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions