This might sound unreasonable but right now I need to negate a type annotation. I mean something like this
an_int : Not[Iterable]
a_string: Iterable
This is because I wrote an overload for a function and mypy does not understand me. My function looks like this...
@overload
def iterable(o: Iterable) -> Literal[True] : ...
@overload
def iterable(o: Any) -> Literal[False] : ...
def iterable(o: Iterable|Any) -> Literal[True, False] :
return isinstance(o, Iterable)
But mypy complains that overload 1 overlaps overload 2 and returns incompatible type.
A negating type annotation could easily solve this by using Not[Iterable]
instead of Any
in overload 2.
Does anyone know how to solve this problem?
For your given example you can use TypeIs introduced in Python 3.13 (and back ported to typing_extensions
).
from typing import Iterable, Any
from typing_extensions import TypeIs
def is_iterable(o: Any) -> TypeIs[Iterable]:
"""
Check if an object is an iterable
See https://stackoverflow.com/questions/1952464
"""
try:
iter(o)
except TypeError:
return False
return True
a_list = [1,2]
if is_iterable(a_list):
reveal_type(a_list) # Revealed type is "builtins.list[builtins.int]"
a_int = 1
if not is_iterable(a_int):
reveal_type(a_int) # Revealed type is "builtins.int"
Check it out at MyPy Playground.
The original answer used TypeGuards
to get the asked behaviour, which was dropped (PEP 724 but it was withdrawn, see reason) in favour of TypeIs
(see discussion).
I am still interested in the negative type annotations, but for a use case that can't be solved with TypeGuard
. But this refers to the long discussion about Intersections
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With