support TypedDict (and Required, NotRequired, ReadOnly)
- [x] Introduce a new
Typevariant, basic support for class-basedTypedDictsyntax- [x] Parse a
TypedDictspecification from the class body - [x] Implement / test assignability to
Mapping[str, object] - [x] Make sure that
TypedDict-based objects do not behave like instances of that class. For example, attribute access should not work. - [x] Implement fallback to
_typeshed._type_checker_internals.TypedDictFallback - [ ] Add
TypedDictsamples to property tests
- [x] Parse a
- [x] Type inference for key accesses (
movie['directory'] -> str) - [x] Validation of writes to keys (synthesized
__setitem__method withkey: Literal["key1"], value: ValueType1?) - [x] Validation of constructor calls (
Movie(name="Blade Runner", year=1982)) - [x] Validation of
{…}literals toTypedDicttypes - [x] Totality
- [x]
RequiredandNotRequired
- [x]
- [x] Implement / write tests for generic
TypedDicts - [x] Implement / write tests for recursive
TypedDicts - [x] Support type inference for
instance.get("known_key")? - [x] PEP 705 support (
ReadOnly) - [x] #1387
- [x] Implement disjointness for
TypedDict. - [ ] Support
TypedDicts in all contexts (returnposition, function call arguments, etc) => #168 - [ ] PEP 728 support ("openness",
extra_items) - [ ] Functional syntax (
Movie = TypedDict('Movie', {'name': str, 'year': int})) - [ ] Support for
Unpack - [ ] Overriding of fields in inheritance
- [ ] Correctness, validation
- [ ] Implement checks based on supported and unsupported operations
- [ ]
TypedDicttype objects cannot be used inisinstancechecks - [ ] Only item definitions in the class body (no methods, for example)
- [ ] Specifying a metaclass is not allowed
- [ ] Can only subclass
TypedDict,TypedDict- based types andGeneric - [ ] Implement / write tests for subclassing of
TypedDict-based classes including multiple inheritance
- [ ] Performance
- [ ] Try out the optimization proposed in https://github.com/astral-sh/ruff/pull/19763#discussion_r2256719857
- [ ] Add a micro-benchmark for large
TypedDicts
- [ ] Implement
.normalize()forTypeDictType. - [ ] Investigate Pydantic-specific performance problems introduced in #21467.
- [ ] Add
TypedDictto property tests. - [ ] Submit a correction to the upstream assignability rules => https://github.com/python/typing/pull/2119
And total=False
TypedDict has a few interesting properties that may affect core concepts in the type system, so I'll be interested to see how you handle them.
The spec says that a TypedDict must be an instance of dict itself, not a subclass. I think that means these test cases should pass:
from typing import Mapping, TypedDict, assert_type
class TD(TypedDict):
a: int
class SubDict[KT, VT](dict[KT, VT]):
pass
def f(x: TD | dict[int, str]):
if isinstance(x, SubDict):
assert_type(x, SubDict[int, str])
else:
assert_type(x, TD | dict[int, str])
def g(x: TD | Mapping[int, str]):
if isinstance(x, dict):
assert_type(x, TD | dict[int, str])
else:
assert_type(x, Mapping[int, str])
(Neither mypy nor pyright currently pass all of this, mostly because they put Any/Unknown in type parameters. I'm not sure there's a good reason for that.)
However, a TypedDict type is not assignable to dict[T, U] for any values of T and U, not even to dict[Any, Any] (spec: https://typing.python.org/en/latest/spec/typeddict.html#assignability). This will have interesting consequences for the issues in #456: it means that narrowing from isinstance(x, dict) can't be implemented by intersecting with some version of forall K, V. dict[K, V].
another piece of functionality that would eventually be nice here is the Unpack keyword
refs
- https://typing.python.org/en/latest/spec/callables.html#unpack-kwargs
- https://peps.python.org/pep-0692/
it's basically a way to use TypedDict's to type the **kwargs of a function
this ends up being very useful in practice for balancing python's flexibility with strict type checking
Moving this to GA because we feel the current feature-set is adequate for beta (though it would be nice to add a few more things if we have time).
Moving this to GA because we feel the current feature-set is adequate for beta (though it would be nice to add a few more things if we have time).
(Note that there are many dictionaries that we do not infer as TypedDict instances right now despite them being annotated as such, but that's really part of https://github.com/astral-sh/ty/issues/168 rather than this issue -- that issue should definitely be a beta priority!)
https://github.com/python/typing/pull/2072 rewrites the TypedDict spec to be much clearer and more concise, which may be useful to anyone working on further features here.
Specifically a good place to focus on would be the full structure of TypedDict as laid out in the introduction to the proposed spec change (a list of items consisting of key, value type, requiredness, mutability; whether there are extra items; and whether or not the extra items are read-only). The internal representation of TypedDict types likely should follow that structure closely. Then you'll also have to write code to recognize the syntactic forms that create these TypedDicts (e.g., keyword args to the TypedDict constructor; the Required/NotRequired/ReadOnly qualifiers; maybe inheritance, though that can wait). Implementing this structure should provide a good basis for implementing other operations on TypedDicts.
Currently this type-checks on the playground (I don't know whether it also type-checks on main):
from typing import TypedDict
class TD(TypedDict):
a: str
x: TD = 1
Even if full bidirectional type checking isn't implemented yet, wouldn't it be possible to at least check whether the right-hand side is a dictionary?
Currently this type-checks on the playground (I don't know whether it also type-checks on main):
(The ty playground automatically gets a fresh deploy on each new commit to main, so it's usually <10 minutes behind main)
anyway: we're working on TypedDict features actively, and we will get an improved TypedDict assignability implementation delivered to you as soon as we can 😄
Moving this ticket to GA, but #1387 into the Beta
Added a checkbox at the top for "Implement .normalize() for TypeDictType."
I'm working on the functional syntax piece.