@@ -449,9 +449,33 @@ def _eval_type(t, globalns, localns, type_params, *, recursive_guard=frozenset()
449449 # prefer_fwd_module flag), so that the default behavior remains more straightforward.
450450 if prefer_fwd_module and t .__forward_module__ is not None :
451451 globalns = None
452- # If there are type params on the owner, we need to add them back, because
453- # annotationlib won't.
454- if owner_type_params := getattr (owner , "__type_params__" , None ):
452+ # # If there are type params on the owner, we need to add them back, because
453+ # # annotationlib won't.
454+ owner_type_params = getattr (owner , "__type_params__" , ())
455+ # TypedDict classes copy the parent type annotations, but do not
456+ # copy parent type params / mro. So, we need to collect them manually here.
457+ if is_typeddict (owner ):
458+ owner_type_params = list (owner_type_params )
459+ mro_stack = list (owner .__orig_bases__ )
460+ seen = {tp .__name__ for tp in owner_type_params }
461+ while mro_stack :
462+ typ = mro_stack .pop (0 )
463+ if is_typeddict (typ ):
464+ mro_stack .extend (typ .__orig_bases__ )
465+ if t not in typ .__annotations__ .values ():
466+ # We only copy __type_params__ for types that own
467+ # this annotation. So, it won't be possible to use
468+ # undeclared type parameters from parent types in children.
469+ continue
470+
471+ base_type_params = getattr (typ , "__type_params__" , ())
472+ for btp in base_type_params :
473+ if btp .__name__ in seen :
474+ continue
475+ owner_type_params .append (btp )
476+ seen .add (btp .__name__ )
477+ owner_type_params = tuple (owner_type_params )
478+ if owner_type_params :
455479 globalns = getattr (
456480 sys .modules .get (t .__forward_module__ , None ), "__dict__" , None
457481 )
0 commit comments