Python TypedDict Arbitrary Key Names with Totality

In PEP 589 Python has introduced type hints for dictionaries. This helps with defining the structure of dictionaries where the keys and the types of the values are well known. TypedDict also supports keys that may not be present using the concept of totality. Using inheritance a dictionary where some keys are not required can be built. The default syntax for defining TypedDict is a class based syntax (example below). Whilst this is easy to understand, it does limit the names of the keys of the dictionary to valid python variable names. If a dictionary needs to include keys with, for example, a dash, the class based syntax no longer is appropriate for defining the TypedDict. There is an alternative syntax (similar to named tuples) that allows for arbitrary key names (example below). However, this syntax does not support inheritance which means that a dictionary with mixed totality cannot be constructed using this syntax alone.

An example for the class based TypedDict definition syntax:

class Movie(TypedDict):
    name: str
    year: int

An example for the named tuple like TypedDict definition syntax:

Movie = TypedDict('Movie', {'name': str, 'year': int})

The solution to the problem is to define 2 separate TypedDicts using the named tuple syntax and then use inheritance and the class based syntax to combine the TypedDicts into a single TypedDict:

_MoveBaseTotal = TypedDict(
    '_MoveBaseTotal',
    {'production-year': int},
    total=True,
)
_MoveBaseNotTotal = TypedDict(
    '_MoveBaseNotTotal',
    {'resolution-4k': bool},
    total=False,
)


class Movie(_MoveBaseTotal, _MoveBaseNotTotal):
    name: str
    year: int

This implies that the Movie dictionary may have the production-year (required, int), name (required, str), year (required, int) and resolution-4k (not required, bool) keys. The following are examples and the corresponding mypy output:

# Invalid dictionary empty
inlvaid_empty: Movie = {}
# mypy:  error: Keys ('production-year', 'name', 'year') missing for TypedDict "Movie"


# Valid dictionary no resolution-4k key
valid_no_resolution_4k: Movie = {
    "name": "movie 1",
    "year": 2000,
    "production-year": 1999,
}
# mypy: no issues found


# Valid dictionary with resolution-4k key
valid_with_resolution_4k: Movie = {
    "name": "movie 1",
    "year": 2000,
    "production-year": 1999,
    "resolution-4k": True,
}
# mypy: no issues found


# Inlvaid dictionary resolution-4k value not bool
invalid_resolution_4k_string: Movie = {
    "name": "movie 1",
    "year": 2000,
    "production-year": 1999,
    "resolution-4k": "yes",
}
# mypy: error: Incompatible types (expression has type "str", TypedDict item "resolution-4k" has type "bool")