I'd like to type-hint JSON objects with an unknown or changing structure (pulled in from external API). I'd like to avoid using Any
or solutions like cast()
as much as possible.
I believe the right hint is:
Json: TypeAlias = dict[str, "Json"] | list["Json"] | str | int | float | bool | None
I find this hint often doesn't work. The following code sample recreates the problem.
import requests
from typing_extensions import TypeAlias
Json: TypeAlias = dict[str, "Json"] | list["Json"] | str | int | float | bool | None
res: requests.models.Response = requests.get(
"https://randomuser.me/api/?results=5&nat=gb"
)
data: Json = res.json()
results: Json = data["results"]
On data["results:]
I get the following complaints from mypy:
No overload variant of "getitem" of "list" matches argument type "str" [call-overload]
Possible overload variants: def getitem(self, SupportsIndex, /) -> Json def getitem(self, slice, /) -> list[Json]
Value of type "dict[str, Json] | list[Json] | str | int | float | bool | None" is not indexable [index]
What am I doing wrong? I've managed to find this GitHub issue which may well contain the solution, but if so, I'm not yet good enough with types to see it.
Thanks!
Any str
is a valid instance of Json
. So when you index a Json
object, as in data["results"]
, mypy
complains that that is not a valid operation on a string. You probably want data: dict[str, Json] = res.json()
. Or even better, a TypedDict
specifying the entire structure of the document.