Сегодня ночью вышел Python 3.8 и аннотации типов получили новые возможности: Если вы ещё не знакомы с аннотациями типов, рекомендую обратить внимание на мои предыдущие статьи (начало, продолжение)
И пока все переживают о моржах, я хочу кратко рассказать о новинках в модуле typing  В Python используется утиная типизация и от классов не требуется наследование от некоего интерфейса, как в некоторых других языках.
К сожалению, до версии 3.8 мы не могли выразить необходимые требования к объекту с помощью аннотаций типов.
PEP 544 призван решить эту проблему. Такие термины как "протокол итератора" или "протокол дескрипторов" уже привычны и используются давно.
Теперь можно описывать протоколы в виде кода и проверять их соответствие на этапе статического анализа.  Стоит отметить, что начиная с Python 3.6 в модуль typing уже входят несколько стандартных протоколов.
Например, SupportsInt (требующего наличие метода __int__), SupportsBytes (требует __bytes__) и некоторых других.  Протокол описывается как обычный класс, наследующийся от Protocol. Он может иметь методы (в том числе с реализацией) и поля.
Реальные классы, реализующие протокол могут наследоваться от него, но это не обязательно.  Мы можете комбинировать протоколы с помощью наследования, создавая новые.
Однако в этом случае вы так же должны явно указать Protocol как родительский класс Протоколы как и обычные классы могут быть Дженериками. Вместо указания в качестве родителей Protocol и Generic[T, S,...] можно просто указать Protocol[T, S,...]  Ещё один важный тип протоколов — self-typed (см. PEP 484). Например, Кроме того, протоколы могут использоваться в тех случаях, когда синтаксиса Callable аннотации недостаточно.
Просто опишите протокол с __call__ методом нужной сигнатуры Хотя протоколы и рассчитаны в первую очередь на использование статическими анализаторами, иногда бывает нужно проверить принадлежность класса нужному протоколу.
Чтобы это было возможно, примените к протоколу декоратор @runtime_checkable и isinstance/issubclass проверки начнут проверять соответствие протоколу  Однако такая возможность имеет ряд ограничений на использование. В частности, не поддерживаются дженерики Для представления структурированных данных обычно используются классы (в частности, дата-классы) или именованные кортежи.
но иногда, например, в случае описания json-структуры бывает полезно иметь словарь с определенным ключами.
PEP 589 вводит понятие TypedDict, который ранее уже был доступен в расширениях от mypy Аналогично датаклассам или типизированным кортежам есть два способа объявить типизированный словарь. Путем наследования или с помощью фабрики: Типизированные словари поддерживают наследование: По умолчанию все ключи словаря обязательны, но можно это отключить передав total=False при создании класса.
Это распространяется только на ключи, описанные в текущем кассе и не затрагивает наследованные Использование TypedDict имеет ряд ограничений. В частности: Кроме того, с таким словарем запрещены такие "небезопасные" операции как .clear или del.
Работа по ключу, который не является литералом, так же может быть запрещена, так как в этом случае невозможно определить ожидаемый тип значения  PEP 591 вводит модификатор final (в виде декоратора и аннотации) для нескольких целей При этом допустим код вида self.id: Final = 123, но только в __init__ методе Literal-тип, определенный в PEP 586 используется когда нужно проверить на конкретным значениям буквально (literally) Например, Literal[42] означает, что в качестве значения ожидается только 42.
Важно, что проверяется не только равенство значения, но и его тип (например, нельзя будет использовать False, если ожидается 0).  В скобках при этом можно передать несколько значений, что эквивалентно использованию Union (типы значений при этом могут не совпадать). В качестве значения нельзя использоваться выражения (например, Literal[1+2]) или значения мутабельных типов.  В качестве одного из полезных примеров использование Literal — функция open(), которая ожидает конкретные значения mode.  Если вы хотите во время работы программы обрабатывать различную информацию о типах (как я),
теперь доступны функции get_origin и get_args. Так, для типа вида X[Y, Z,...] в качестве origin будет возвращён тип X, а в качестве аргументов — (Y, Z, ...)
Стоит отметить, что если X является алиасом для встроенного типа или типа из модуля collections, то он будет заменен на оригинал.  К сожалению, функцию для __parameters__ не сделали