@@ -633,12 +633,22 @@ def _issubclass(cls: GenericType, cls_check: GenericType, instance: Any = None)
633633 raise TypeError (msg ) from te
634634
635635
636- def does_obj_satisfy_typed_dict (obj : Any , cls : GenericType ) -> bool :
636+ def does_obj_satisfy_typed_dict (
637+ obj : Any ,
638+ cls : GenericType ,
639+ * ,
640+ nested : int = 0 ,
641+ treat_var_as_type : bool = True ,
642+ treat_mutable_obj_as_immutable : bool = False ,
643+ ) -> bool :
637644 """Check if an object satisfies a typed dict.
638645
639646 Args:
640647 obj: The object to check.
641648 cls: The typed dict to check against.
649+ nested: How many levels deep to check.
650+ treat_var_as_type: Whether to treat Var as the type it represents, i.e. _var_type.
651+ treat_mutable_obj_as_immutable: Whether to treat mutable objects as immutable. Useful if a component declares a mutable object as a prop, but the value is not expected to change.
642652
643653 Returns:
644654 Whether the object satisfies the typed dict.
@@ -648,19 +658,35 @@ def does_obj_satisfy_typed_dict(obj: Any, cls: GenericType) -> bool:
648658
649659 key_names_to_values = get_type_hints (cls )
650660 required_keys : frozenset [str ] = getattr (cls , "__required_keys__" , frozenset ())
661+ is_closed = getattr (cls , "__closed__" , False )
662+ extra_items_type = getattr (cls , "__extra_items__" , Any )
651663
652- if not all (
653- isinstance (key , str )
654- and key in key_names_to_values
655- and _isinstance (value , key_names_to_values [key ])
656- for key , value in obj .items ()
657- ):
658- return False
659-
660- # TODO in 3.14: Implement https://peps.python.org/pep-0728/ if it's approved
664+ for key , value in obj .items ():
665+ if is_closed and key not in key_names_to_values :
666+ return False
667+ if nested :
668+ if key in key_names_to_values :
669+ expected_type = key_names_to_values [key ]
670+ if not _isinstance (
671+ value ,
672+ expected_type ,
673+ nested = nested - 1 ,
674+ treat_var_as_type = treat_var_as_type ,
675+ treat_mutable_obj_as_immutable = treat_mutable_obj_as_immutable ,
676+ ):
677+ return False
678+ else :
679+ if not _isinstance (
680+ value ,
681+ extra_items_type ,
682+ nested = nested - 1 ,
683+ treat_var_as_type = treat_var_as_type ,
684+ treat_mutable_obj_as_immutable = treat_mutable_obj_as_immutable ,
685+ ):
686+ return False
661687
662688 # required keys are all present
663- return required_keys .issubset (required_keys )
689+ return required_keys .issubset (frozenset ( obj ) )
664690
665691
666692def _isinstance (
@@ -721,7 +747,13 @@ def _isinstance(
721747 # cls is a typed dict
722748 if is_typeddict (cls ):
723749 if nested :
724- return does_obj_satisfy_typed_dict (obj , cls )
750+ return does_obj_satisfy_typed_dict (
751+ obj ,
752+ cls ,
753+ nested = nested - 1 ,
754+ treat_var_as_type = treat_var_as_type ,
755+ treat_mutable_obj_as_immutable = treat_mutable_obj_as_immutable ,
756+ )
725757 return isinstance (obj , dict )
726758
727759 # cls is a float
0 commit comments