Consider the following code:
def optional_int() -> int | None: ...
x = optional_int()
if x is None:
x = 0
else:
pass
reveal_type(x) # revealed: int
We correctly infer int for x in the final line. This works because we union the types of both branches. The if branch has an explicit definition of x with type Literal[0]. And the empty else branch has a type of (int | None) & ~None = int for x, thanks to narrowing. The union of these two, Literal[0] | int, then simplifies to int.
However, if we remove the empty else branch, we get a type of x: int | None in the final line. This should be fixed. We should not union Literal[0] from the if branch with what we had before (int | None), but rather apply the inverted narrowing condition to the pre-if type of x, just as if we had an empty else branch.
def optional_int() -> int | None: ...
x = optional_int()
if x is None:
x = 0
reveal_type(x) # revealed: int | None
Consider the following code:
We correctly infer
intforxin the final line. This works because we union the types of both branches. Theifbranch has an explicit definition ofxwith typeLiteral[0]. And the emptyelsebranch has a type of(int | None) & ~None = intforx, thanks to narrowing. The union of these two,Literal[0] | int, then simplifies toint.However, if we remove the empty
elsebranch, we get a type ofx: int | Nonein the final line. This should be fixed. We should not unionLiteral[0]from theifbranch with what we had before (int | None), but rather apply the inverted narrowing condition to the pre-iftype ofx, just as if we had an emptyelsebranch.