How to create a user-defined type assertion in Python?
How can one create a function that narrows the type of a variable (for static type checkers) in a similar way to isinstance
?
For example, ComplexTypeAssertion
just narrows the type at runtime but not for static checks:
def MyFunction(myData: object) -> object:
if isinstance(myData, list):
reveal_type(myData) # Revealed type is 'builtins.list[Any]'
if ComplexTypeAssertion(myData): # <<< How to make MyPy understand this?
reveal_type(myData) # Revealed type is 'builtins.object'
def ComplexTypeAssertion(data: object) -> bool:
return isinstance(data, list) and all(isinstance(value, str) for value in data)
How could I define ComplexTypeAssertion
such that static analysis tools would understand the type?
Obviously, this is a toy example, real life examples would be more complex. It would be very useful in situations where I want to assert that some data follows a TypedDict
construct or other typing constructs.
Mypy generally only understands a few specific ways of narrowing a type, such as using isinstance
or if x is not None
. However, in Python >=3.10, PEP 647 provides a solution to this problem with the new typing.TypeGuard
feature. Now, you are able to write your ComplexTypeAssertion
function like so, and type checkers will understand that data
is guaranteed to be of type list[str]
if the function returns True
:
from typing import TypeGuard
def ComplexTypeAssertion(data: object) -> TypeGuard[list[str]]:
return isinstance(data, list) and all(isinstance(value, str) for value in data)
TypeGuard
is also available for Python <3.10 via the semi-official typing-extensions
library on PyPI, which provides backports of newer typing constructs to earlier versions of Python. (If you're using Mypy, you'll find that typing-extensions
is already a dependency of Mypy, so importing something from typing-extensions
won't even necessarily mean that you have to add an additional dependency to your project.)