@@ -895,15 +895,25 @@ c: Union[A, B] = {'@type': 'a-type', 'a': 'Test'}
895895reveal_type(c) # N: Revealed type is "Union[TypedDict('__main__.A', {'@type': Literal['a-type'], 'a': builtins.str}), TypedDict('__main__.B', {'@type': Literal['b-type'], 'b': builtins.int})]"
896896[builtins fixtures/dict.pyi]
897897
898- [case testTypedDictUnionAmbiguousCase ]
898+ [case testTypedDictUnionAmbiguousCaseBothMatch ]
899899from typing import Union, Mapping, Any, cast
900900from typing_extensions import TypedDict, Literal
901901
902- A = TypedDict('A', {'@type': Literal['a-type'], 'a': str})
903- B = TypedDict('B', {'@type': Literal['a-type'], 'a': str})
902+ A = TypedDict('A', {'@type': Literal['a-type'], 'value': str})
903+ B = TypedDict('B', {'@type': Literal['b-type'], 'value': str})
904+
905+ c: Union[A, B] = {'@type': 'a-type', 'value': 'Test'}
906+ [builtins fixtures/dict.pyi]
907+
908+ [case testTypedDictUnionAmbiguousCaseNoMatch]
909+ from typing import Union, Mapping, Any, cast
910+ from typing_extensions import TypedDict, Literal
904911
905- c: Union[A, B] = {'@type': 'a-type', 'a': 'Test'} # E: Type of TypedDict is ambiguous, could be any of ("A", "B") \
906- # E: Incompatible types in assignment (expression has type "Dict[str, str]", variable has type "Union[A, B]")
912+ A = TypedDict('A', {'@type': Literal['a-type'], 'value': int})
913+ B = TypedDict('B', {'@type': Literal['b-type'], 'value': int})
914+
915+ c: Union[A, B] = {'@type': 'a-type', 'value': 'Test'} # E: Type of TypedDict is ambiguous, none of ("A", "B") matches cleanly \
916+ # E: Incompatible types in assignment (expression has type "Dict[str, str]", variable has type "Union[A, B]")
907917[builtins fixtures/dict.pyi]
908918
909919-- Use dict literals
@@ -2786,3 +2796,79 @@ TDC = TypedDict("TDC", {"val": int, "next": Optional[Self]}) # E: Self type can
27862796
27872797[builtins fixtures/dict.pyi]
27882798[typing fixtures/typing-typeddict.pyi]
2799+
2800+ [case testUnionOfEquivalentTypedDictsInferred]
2801+ from typing import TypedDict, Dict
2802+
2803+ D = TypedDict("D", {"foo": int}, total=False)
2804+
2805+ def f(d: Dict[str, D]) -> None:
2806+ args = d["a"]
2807+ args.update(d.get("b", {})) # OK
2808+ [builtins fixtures/dict.pyi]
2809+ [typing fixtures/typing-typeddict.pyi]
2810+
2811+ [case testUnionOfEquivalentTypedDictsDeclared]
2812+ from typing import TypedDict, Union
2813+
2814+ class A(TypedDict, total=False):
2815+ name: str
2816+ class B(TypedDict, total=False):
2817+ name: str
2818+
2819+ def foo(data: Union[A, B]) -> None: ...
2820+ foo({"name": "Robert"}) # OK
2821+ [builtins fixtures/dict.pyi]
2822+ [typing fixtures/typing-typeddict.pyi]
2823+
2824+ [case testUnionOfEquivalentTypedDictsEmpty]
2825+ from typing import TypedDict, Union
2826+
2827+ class Foo(TypedDict, total=False):
2828+ foo: str
2829+ class Bar(TypedDict, total=False):
2830+ bar: str
2831+
2832+ def foo(body: Union[Foo, Bar] = {}) -> None: # OK
2833+ ...
2834+ [builtins fixtures/dict.pyi]
2835+ [typing fixtures/typing-typeddict.pyi]
2836+
2837+ [case testUnionOfEquivalentTypedDictsDistinct]
2838+ from typing import TypedDict, Union, Literal
2839+
2840+ class A(TypedDict):
2841+ type: Literal['a']
2842+ value: bool
2843+ class B(TypedDict):
2844+ type: Literal['b']
2845+ value: str
2846+
2847+ Response = Union[A, B]
2848+ def method(message: Response) -> None: ...
2849+
2850+ method({'type': 'a', 'value': True}) # OK
2851+ method({'type': 'b', 'value': 'abc'}) # OK
2852+ method({'type': 'a', 'value': 'abc'}) # E: Type of TypedDict is ambiguous, none of ("A", "B") matches cleanly \
2853+ # E: Argument 1 to "method" has incompatible type "Dict[str, str]"; expected "Union[A, B]"
2854+ [builtins fixtures/dict.pyi]
2855+ [typing fixtures/typing-typeddict.pyi]
2856+
2857+ [case testUnionOfEquivalentTypedDictsNested]
2858+ from typing import TypedDict, Union
2859+
2860+ class A(TypedDict, total=False):
2861+ foo: C
2862+ class B(TypedDict, total=False):
2863+ foo: D
2864+ class C(TypedDict, total=False):
2865+ c: str
2866+ class D(TypedDict, total=False):
2867+ d: str
2868+
2869+ def foo(data: Union[A, B]) -> None: ...
2870+ foo({"foo": {"c": "foo"}}) # OK
2871+ foo({"foo": {"e": "foo"}}) # E: Type of TypedDict is ambiguous, none of ("A", "B") matches cleanly \
2872+ # E: Argument 1 to "foo" has incompatible type "Dict[str, Dict[str, str]]"; expected "Union[A, B]"
2873+ [builtins fixtures/dict.pyi]
2874+ [typing fixtures/typing-typeddict.pyi]
0 commit comments