Skip to content

Conversation

@brianschubert
Copy link
Member

Closes #16160

Before

from typing_extensions import Never, TypeGuard

def guard(x: object) -> TypeGuard[Never]:
    ...

a: object

if guard(a):
    reveal_type(a)  # N: Revealed type is "Never"
else:
    reveal_type(a)  # N: Revealed type is "builtins.object"

assert guard(a)
b = 0  # reachable

After

from typing_extensions import Never, TypeGuard

def guard(x: object) -> TypeGuard[Never]:
    ...

a: object

if guard(a):
    reveal_type(a)  # E: Statement is unreachable
else:
    reveal_type(a)  # N: Revealed type is "builtins.object"

assert guard(a)
b = 0  # E: Statement is unreachable

@brianschubert brianschubert changed the title Signal unreachabliliy when narrowing with TypeGuard[Never] or TypeIs[Never] Signal unreachability when narrowing with TypeGuard[Never] or TypeIs[Never] Sep 25, 2024
@github-actions
Copy link
Contributor

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

Copy link
Collaborator

@hauntsaninja hauntsaninja left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm, shouldn't the TypeIs case already work?

(Is there a valid use case for this?)

@brianschubert
Copy link
Member Author

Hm, shouldn't the TypeIs case already work?

D'oh! It does. Or at least mostly works. It does detect unreachability in the basic case, but looking a little closer, it has some funny behavior in the edge case where the prior type involves Never:

def guard(x: object) -> TypeIs[Never]: ...

a: Never

if guard(a):
    reveal_type(a)  # Considered reachable, N: Revealed type is "Never"    
else:
    reveal_type(a)  # Curiously, no note or error!

assert guard(a)
reveal_type(a)      # Considered reachable, N: Revealed type is "Never"    

Likewise, it will also let you narrow Never | object to object. A little weird, but I'm not sure that these cases are something worth caring about on their own.

(Is there a valid use case for this?)

Honestly, I don't know. I can speculate ways it could be used, but I doubt any of them would pass a code review :)

I mainly see this as nice from a consistency perspective. This makes Type{Guard,Is}[Never] behave exactly the same as Literal[False], even in the edge cases with Never.

Maybe this would be useful in a second-order case, where the argument to Type{Guard,Is} comes from something more complicated (think conditionally defined type aliases, or generated code), and where having unusual behavior for Never could be an extra source of confusion?

Please feel free to close if you don't think this is worth pursing :)

@brianschubert
Copy link
Member Author

Coming back to this a month later, I don't think this is actually worth changing. The use cases I had in mind don't really pan out, so I don't think this is worth the added complexity. I'm going close this for now. We can always revisit if a user finds an actual use case

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

(🎁) Report unreachable when TypeGuard[Never]

2 participants