Search code examples
pythonjsonmypypython-typing

Type hinting a JSON object in Python


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

Problem

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]

Question

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!


Solution

  • 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.