Repository: samuelcolvin/pydantic (python)

Issue:
`Config.smart_union` doesn't work with `TypedDict`
### Checks

* [x] I added a descriptive title to this issue
* [x] I have searched (google, github) for similar issues and couldn't find anything
* [x] I have read and followed [the docs](https://pydantic-docs.helpmanual.io/) and still think this is a bug

<!-- Sorry to sound so draconian, but every second saved replying to issues is time spend improving pydantic :-) -->

# Bug

Output of `python -c "import pydantic.utils; print(pydantic.utils.version_info())"`:
```
             pydantic version: 1.9.0a1
            pydantic compiled: False
               python version: 3.10.0 (tags/v3.10.0:b494f59, Oct  4 2021, 19:00:18) [MSC v.1929 64 bit (AMD64)]
                     platform: Windows-10-10.0.22000-SP0
     optional deps. installed: ['typing-extensions']
```
<!-- or if you're using pydantic prior to v1.3, manually include: OS, python version and pydantic version -->

<!-- Please read the [docs](https://pydantic-docs.helpmanual.io/) and search through issues to
confirm your bug hasn't already been reported. -->

<!-- Where possible please include a self-contained code snippet describing your bug: -->

```py
class Dict1(TypedDict):
    foo: str


class Dict2(TypedDict):
    bar: str


class M(BaseModel):
    d: Union[Dict2, Dict1]

    class Config:
        smart_union = True


M(d=dict(foo='baz'))  # raises TypeError: isinstance() arg 2 must be a type, a tuple of types, or a union
```


Pull request:
fix: smart union with typeddict
<!-- Thank you for your contribution! -->
<!-- Unless your change is trivial, please create an issue to discuss the change before creating a PR -->
<!-- See https://pydantic-docs.helpmanual.io/contributing/ for help on Contributing -->

## Change Summary

<!-- Please give a short summary of the changes. -->

## Related issue number
fix #3541
<!-- Are there any issues opened that will be resolved by merging this change? -->
<!-- WARNING: please use "fix #123" style references so the issue is closed when this PR is merged. -->

## Checklist

* [x] Unit tests for the changes exist
* [x] Tests pass on CI and coverage remains at 100%
* [ ] ~Documentation reflects the changes where applicable~
* [ ] ~`changes/<pull request or issue id>-<github username>.md` file added describing change
  (see [changes/README.md](https://github.com/samuelcolvin/pydantic/blob/master/changes/README.md) for details~
* [x] My PR is ready to review, **please add a comment including the phrase "please review" to assign reviewers**


Patch:
diff --git a/pydantic/fields.py b/pydantic/fields.py
index ff7b962f7..44a3aa10b 100644
--- a/pydantic/fields.py
+++ b/pydantic/fields.py
@@ -50,6 +50,7 @@ from .utils import (
     ValueItems,
     get_discriminator_alias_and_values,
     get_unique_discriminator_alias,
+    lenient_isinstance,
     lenient_issubclass,
     sequence_like,
     smart_deepcopy,
@@ -1048,7 +1049,7 @@ class ModelField(Representation):
                             return v, None
                     except TypeError:
                         # compound type
-                        if isinstance(v, get_origin(field.outer_type_)):
+                        if lenient_isinstance(v, get_origin(field.outer_type_)):
                             value, error = field.validate(v, values, loc=loc, cls=cls)
                             if not error:
                                 return value, None
diff --git a/pydantic/utils.py b/pydantic/utils.py
index ec94f76c7..d4ca69395 100644
--- a/pydantic/utils.py
+++ b/pydantic/utils.py
@@ -53,6 +53,7 @@ __all__ = (
     'import_string',
     'sequence_like',
     'validate_field_name',
+    'lenient_isinstance',
     'lenient_issubclass',
     'in_ipython',
     'deep_update',
@@ -163,6 +164,13 @@ def validate_field_name(bases: List[Type['BaseModel']], field_name: str) -> None
             )
 
 
+def lenient_isinstance(o: Any, class_or_tuple: Union[Type[Any], Tuple[Type[Any], ...]]) -> bool:
+    try:
+        return isinstance(o, class_or_tuple)
+    except TypeError:
+        return False
+
+
 def lenient_issubclass(cls: Any, class_or_tuple: Union[Type[Any], Tuple[Type[Any], ...]]) -> bool:
     try:
         return isinstance(cls, type) and issubclass(cls, class_or_tuple)


Test patch:
diff --git a/tests/test_types.py b/tests/test_types.py
index 2a3161316..bbf4c23b1 100644
--- a/tests/test_types.py
+++ b/tests/test_types.py
@@ -27,7 +27,7 @@ from typing import (
 from uuid import UUID
 
 import pytest
-from typing_extensions import Literal
+from typing_extensions import Literal, TypedDict
 
 from pydantic import (
     UUID1,
@@ -3120,6 +3120,22 @@ def test_smart_union_compouned_types_edge_case():
     assert Model(x=[1, '2']).x == ['1', '2']
 
 
+def test_smart_union_typeddict():
+    class Dict1(TypedDict):
+        foo: str
+
+    class Dict2(TypedDict):
+        bar: str
+
+    class M(BaseModel):
+        d: Union[Dict2, Dict1]
+
+        class Config:
+            smart_union = True
+
+    assert M(d=dict(foo='baz')).d == {'foo': 'baz'}
+
+
 @pytest.mark.parametrize(
     'value,result',
     (