Search code examples
pythonpycharmpython-typingmypy

Why is mypy/PyCharm/etc not detecting type errors for Type[T]?


Consider the following code:

def verify(schema: Type[T], data: T) -> None:
    pass

verify(int, "3")
verify(float, "3")
verify(str, "3")

I would expect the first two verify() calls to show up as a type error, and the last one to not.

However, none of them show up with type errors, in PyCharm and in mypy. I tried enabling every possible flag for strictness and error codes, yet nothing.

How can I get a type-checker to type-check this? Why does it fail?

Libraries like apischema rely on functionality like this for type-checking, e.g., apischema.serialize(MyDataclass, my_dataclass), but that doesn't work either.


Solution

  • The type bound to T is not determined by any single argument; it is chosen in a way that satisfies the call. Since object is the closest common supertype of int-and-str or float-and-str), T is bound to object in those cases. (For verify(str, "3"), T is bound to str for the same reason: it's the trivial closest supertype of str and str).

    You can see this by having verify return schema, then asking mypy what the return value has.

    def verify(schema: Type[T], data: T) -> Type[T]:
        return schema
    
    reveal_type(verify(int, "3"))  # builtins.object
    reveal_type(verify(float, "3"))  # builtins.object
    reveal_type(verify(str, "3"))  # builtins.str
    

    If you can enumerate the types you expect to pass to schema, you can partially solve the problem by making T a constrained type variable.

    T = TypeVar('T', int, str, float)
    
    def verify(schema: Type[T], data: T) -> None:
        ...
    

    Since T can no longer be bound to object, your first two examples will fail to type-check as desired.


    Note that something like the following will still pass:

    class MyFloat(float):
        pass
    
    
    verify(MyFloat, 3.14)
    

    because T will be bound to float, the closest common superclass of float and MyFloat. (This isn't really any different from the case with object, but tends to "look" less surprising.)