Description
The type inferred for attribute x
in the fragment below is unexpected (I was using --strict-optional
):
class A:
x = None
y = None
def __init__(self): # Note: no annotation
self.x = 1
def g(self) -> None:
self.y = 1
a = A()
reveal_type(a.x) # None <---- unexpected
reveal_type(a.y) # Union[int, None]
if a.x is not None:
1 + '' # No error, because mypy considers this unreachable
Union[None, Any]
would be a better inferred type for A.x
. It would work around the false negative. This would be consistent with how global variables work:
x = None
def f():
global x
x = 1
reveal_type(x) # Union[Any, None]
When not using strict optional checking, the type of A.x
is currently also None
. Any
would be a better inferred type in that case.
Another alternative would be to require annotations for both x
and y
, since inferring a useful (non-None
) type requires non-local context. This would be my preference if we didn't have existing code to worry about -- this could require a large number of additional annotations for code that currently type checks cleanly. A final option would be to only require an annotation for y
and infer Union[None, Any]
for x
, since x
is initialized in an unannotated method.
[Note that use of non-local context (outside current scope) can make fine-grained incremental checking harder to implement.]