pythonmypypython-typingtype-narrowing

Error in literal type checking by the mypy tool


I integrated mypy into my code, but there is error in type checking of the literal:

error: Item "None" of "Optional[Type[Temp]]" has no attribute "pankaj"  [union-attr]

It is treating b as always Optional[Type[Temp]]. When a is True, then b should not be Optional, otherwise b is Optional. However, mypy gives the same error in both branches:

class Temp:
    @staticmethod
    def pankaj():
        print("pankaj")

def accepts_only_int(x: int) -> Union[tuple[Literal[True],Type[Temp]],tuple[Literal[False],Optional[Type[Temp]]]]:
    return (False,Temp)

a, b=accepts_only_int(6)

if a:
    print(b.pankaj())
else:
    print(a,b.pankaj())

Solution

  • mypy cannot track dependencies of several variables, it's an implementation limitation. To work this around, do not unpack accepts_only_int - assign it to one variable a and test if a[0]. Then you're not referring to several variables, but narrow a tuple by it's item type from union. You can unpack in conditional branches later:

    from typing import Literal
    
    class Temp:
        @staticmethod
        def pankaj() -> None:
            print("pankaj")
    
    def accepts_only_int(x: int) -> tuple[Literal[True], type[Temp]] | tuple[Literal[False], type[Temp] | None]:
        return (False, Temp)
    
    a = accepts_only_int(6)
    
    if a[0]:
        _, temp = a
        print(temp.pankaj())
    else:
        print(a[0], a[1].pankaj())  # E: Item "None" of "Optional[Type[Temp]]" has no attribute "pankaj"  [union-attr]
    

    This has only one mypy error, as expected. Here's a playground link.