Description
Consider this code (run with --strict-optional
):
from typing import Optional
def f():
# untyped function; often from silent import
return 0 # always returns an int
def g(x: Optional[int]) -> int
if x is None:
x = f()
return x # E: Incompatible return value type (got "Optional[int]", expected "int")
Mypy doesn't infer that x
is not None
in g
. I think this a bug -- mypy
should be permissive here.
If we wanted to fix this, we'd run into a problem: what type should x
have
after x = f()
? We don't want it to be Any
, because we don't want that
behavior in the general case. It shouldn't be int
-- f()
could be intended
to unconditionally return None
instead. To fix this, I think we need to
introduce a new special type, which I will call AnyUnion
. AnyUnion[A, B, C]
would represent a type which is allowed to be used as any of A
, B
, or
C
(similarly to how Any
is allowed to be used as any type). In this case,
we could have x
's type bound to AnyUnion[int, None]
, which would fix this
error.
Other helpful uses of AnyUnion
:
pow
will no longer need to returnAny
in builtins. Instead, it could
return the much less broadAnyUnion[int, float]
.AnyUnion[X, None]
would provide a per-variable opt-out of strict None
checking. This could be helpful for classes with delayed initialization where
a lot of effort would be needed to refactor them into a state that would work
properly with strict None checking.
Cons:
- Additional complexity of the type system affects implementation complexity and
makes things slightly harder for users to understand. - The motivating problem also exists with subclasses (with the way the binder
currently works), butAnyUnion
doesn't help much with that.