Skip to content
This repository has been archived by the owner on May 28, 2022. It is now read-only.

Latest commit

 

History

History
219 lines (151 loc) · 5.92 KB

pep-9999.rst

File metadata and controls

219 lines (151 loc) · 5.92 KB

PEP: 9999 Title: Details of generic support for TypedDict Author: Samodya Abeysiriwardane <hi at sransara.com> Status: Draft Type: Standard Track Content-Type: text/x-rst Created: 19-Jul-2021 Python-Version: 3.11 Post-History:

Abstract

PEP 589 [1] introduces a type constructor TypedDict to support the use case where a dictionary object has a specific set of string keys, each with a value of a specific type. But it does not specify the details of supporting generic TypedDicts, where the type of a value can be parameterized. This PEP specifies the details of a type parameterized TypedDicts as an extension of PEP 589 [1].

Motivation

TypedDict is a great choice to give a precise type for pure data containers. Almost all container data types of the standard library support generics. TypedDicts should also support generics.

PEP 589 [1] sets a requirement that a TypedDict can only inherit from a TypedDict. This PEP proposes to relax that requirement to allow TypedDict with Generic and layout the details of allowing generics.

Following is few example usecases for having generic TypedDicts.

Using type annotations to convey relationships between keys.

class Page(TypedDict, Generic[T]):
uid: Hash[T] desc: str item: T

An example where the function on container type does not concern with the concrete type of the contents.

def get_items(*pages: Page[T]) -> List[T]:
return [page["item"] for page in pages]

Another usage example in annotating polymorphic JSON responses.

class Item(TypedDict):
type: str
class ValueItem(Item):
value: int

T = TypeVar('T')

class ItemPager(TypedDict, Generic[T]):
next_page: Optional[str] items: List[T]
def get_value_items() -> List[ValueItem]:

value_items: List[ValueItem] = [] url = "https://example.com/value_items" while url:

response = requests.get(url) pager: ItemPager[ValueItem] = response.json() url = pager['next_page'] value_items += pager['items']

return value_items

Specification

This PEP is an extension of PEP 589 [1], hence details specified here for TypedDicts will be in addition to what is specified in PEP 589 [1].

Class based syntax

Class based syntax for TypedDicts will follow the same specification as user defined generic types in PEP 484 [2] and class based syntax specification detailed in PEP 589 [1].

from typing import TypedDict, Generic, TypeVar

T = TypeVar("T")

class Pager(TypedDict, Generic[T]):
next_page: str items: List[T]

Generic[T] as a base class defines that the class Pager takes a single type parameter T. Pager[int] is a TypedDict that has a key items with value type List[int].

Alternative syntax

TODO: This section needs be re-thought out because in implementation _collect_type_vars cannot see inside ForwardRef`s to find `TypeVars.

The following alternative syntax is semantically equivalent to the previous class based syntax example. Order of Generic subscript TypeVars will be in the order of their appearance. For Python versions before 3.7 where dict item order is not guaranteed, an ordered dictionary needs to be used with type variables.

from typing import TypedDict

T = TypeVar("T")

Pager = TypedDict("Pager", {
'next_page': str, 'items': List[T],

})

Inheritance

A TypedDict can only inherit from another TypedDict or TypedDict with Generic. This is in contrast to PEP 589 [1], which specifies that a TypedDict can only inherit from a TypedDict.

Subclassing a generic TypedDict without specifying type parameters assumes Any for each position.

All other details of TypedDict inheritance from PEP 589 [1] applies.

Inheriting a generic TypedDict with specified parameters, would inherit annotations with type variable parameters replaced by those specified parameters.

class A(TypedDict, Generic[T, VT], total=False):
a1: List[T] a2: VT
class B(A[KT, int]):
b: KT

In the above example B inherits a2 with type int.

Using generic TypedDict types

Here is an example of how the type Pager can be used.

pager: Pager[str] = {
'next_page': 'https://example.com/value_items?p=2', 'items': ['item 1', 'item2',]

}

If the type parameter is not specified, type checker can follow the same behavior as how it treats other generic instances without a type parameter.

Type consistency