Does typing.TypedDict
allow extra keys? Does a value pass the typechecker, if it has keys which are not present on the definition of the TypedDict?

- 6,359
- 2
- 30
- 55

- 4,925
- 3
- 23
- 44
2 Answers
It depends.
PEP-589, the specification of TypedDict
, explicitely forbids extra keys:
Extra keys included in TypedDict object construction should also be caught. In this example, the director key is not defined in Movie and is expected to generate an error from a type checker:
m: Movie = dict( name='Alien', year=1979, director='Ridley Scott') # error: Unexpected key 'director'
[highlighting by me]
The typecheckers mypy, pyre, pyright implement this according to the specification.
However, it is possible that a value with extra keys is accepted. This is because subtyping of TypedDicts is allowed, and the subtype might implement the extra key. PEP-589 only forbids extra keys in object construction, i.e. in literal assignment. As any value that complies with a subtype is always deemed to comply with the parent type and can be upcasted from the subtype to the parent type, an extra key can be introduced through a subtype:
from typing import TypedDict
class Movie(TypedDict):
name: str
year: int
class MovieWithDirector(Movie):
director: str
# This is illegal:
movie: Movie = {
'name': 'Ash is purest white',
'year': 2018,
'director': 'Jia Zhangke',
}
# This is legal:
movie_with_director: MovieWithDirector = {
'name': 'Ash is purest white',
'year': 2018,
'director': 'Jia Zhangke',
}
# This is legal, MovieWithDirector is a subtype of Movie
movie: Movie = movie_with_director
In the example above, we see that the same value can sometimes be considered complying with Movie
by the typing system, and sometimes not.
As a consequence of subtyping, typing a parameter as a certain TypedDict is not a safeguard against extra keys, because they could have been introduced through a subtype.
If your code is sensitive with regard to the presence of extra keys (for instance, if it makes use of param.keys()
, param.values()
or len(param)
on the TypedDict
parameter param
), this could lead to problems when extra keys are present. A solution to this problem is to either handle the exceptional case that extra keys are actually present on the parameter or to make your code insensitive against extra keys.
If you want to test that your code is robust against extra keys, you cannot simply add a key in the test value:
def some_movie_function(movie: Movie):
# ...
def test_some_movie_function():
# this will not be accepted by the type checker:
result = some_movie_function({
'name': 'Ash is purest white',
'year': 2018,
'director': 'Jia Zhangke',
'genre': 'drama',
})
Workarounds are to either make the type checkers ignore the line or to create a subtype for your test, introducing the extra keys only for your test:
class ExtendedMovie(Movie):
director: str
genre: str
def test_some_movie_function():
extended_movie: ExtendedMovie = {
'name': 'Ash is purest white',
'year': 2018,
'director': 'Jia Zhangke',
'genre': 'drama',
}
result = some_movie_function(test_some_movie_function)
# run assertions against result
}

- 4,925
- 3
- 23
- 44
If you know the optionals keys, you can use total=False
:
class Movie(TypedDict):
name: str
year: int
class MovieWithDirector(Movie, total=False):
director: str
x: MovieWithDirector = {'name':''} # error: missing "year"
y: MovieWithDirector = {'name':'', 'year':1} # ok
z: MovieWithDirector = {'name':'', 'year':1, 'director':''} # ok
w: MovieWithDirector = {'name':'', 'year':1, 'foo':''} # error key "foo"

- 1,816
- 9
- 10
-
4The question whether other additional fields (beyond the one named on `MovieWithDirector`) will be accepted by typecheckers, still remains after your post. – Jonathan Scholbach Apr 11 '22 at 08:40
-
Ok, typechecker will not accept arbitrary fields, but if you know them you can make them optional with `total=False`. – hussic Apr 11 '22 at 16:03
-
That the typechecker will not accept arbitrary fields is partly wrong, see my answer. I posted the question and answer in order to make users of TypedDict aware of this. – Jonathan Scholbach Apr 11 '22 at 17:25
-
This technique with inheritance is useful for many cases, but there is a serious limitation that I have experienced. If you want any keys with names that are reserved keywords (like `async` or `class`), this won't work because Python won't let you use those keywords as field names. – wch Jun 16 '22 at 03:46