Type hints
How Home Assistant Core uses type hints and mypy, and how to opt a module in to strict checking.
Type hints make code easier to understand for the next person, and they let mypy find bugs before your users do. Core checks every module with mypy, in the Git hooks and in CI.
Type hints are not required in every integration yet, but they are strongly encouraged, and the goal is full coverage. In tests, reviewers do expect them: give every parameter of a test function a type, and prefer concrete types like HomeAssistant or MockConfigEntry over Any.
async def test_setup(
hass: HomeAssistant,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test setting up the integration."""Python 3.14 evaluates annotations lazily, so you can refer to a class that is defined further down the file without quotes.
Typing a module that has no hints at all is a lot of work. MonkeyType can do a first pass: it watches the calls while the tests run, and suggests hints from the types it saw. Core has a script for it. MonkeyType is not installed by default, so install it first with uv pip install monkeytype.
-
Run the tests of your module under MonkeyType:
Shellscript/monkeytype tests/components/example/test_sensor.py -
Look at the hints it suggests, with
monkeytype stub homeassistant.components.example.sensor. -
If they look reasonable, apply them with
monkeytype apply homeassistant.components.example.sensor. -
Check the diff and fix what it got wrong. It only knows the types the tests happened to use.
This works best on modules without any hints. Applying it to a module that already has some can fail.
By default, mypy accepts code that is only partly typed. A module that is fully typed can opt in to strict checking, which also fails on missing hints and Any creeping in.
To opt in, add the module to the .strict-typing file in the root of the repository, in alphabetical order. For an integration, that is a line like homeassistant.components.example.*. Then run hassfest to update mypy.ini:
python3 -m script.hassfest -p mypy_configCommit both files. Integrations on the Platinum tier of the integration quality scale must be strictly typed.
Sometimes you know more than the type checker. For example, a value that can be None in general, but never at this point in the code. Narrow the type with an assert inside a TYPE_CHECKING block, so it exists for the type checker only and never runs:
from typing import TYPE_CHECKING
if TYPE_CHECKING:
assert self.coordinator.data is not NoneOnly do this when changing the annotations themselves is impossible or very cumbersome.