We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
StrawberryUnion's type resolver does not prioritise types in the Union
## Describe the Bug

StrawberryUnion's type resolver iterates over all known types and returns the first type which successfully implements the returned object from the resolver.

Due to limitations of `TypeDefinition.is_implemented_by` when checking generic fields, this check might succeed prematurely for an object with the same top level structure.

If the wrong type is selected a `UnallowedReturnTypeForUnion` error is raised shortly afterwards.

Potentially it might be possible to improve `TypeDefinition.is_implemented_by` check for generic fields but that is beyond the scope of this issue.

This issue suggests a simple fix of first checking the types given in the union before falling back to checking all types. This might also be a small performance improvement if it is true that in most cases the correct type is listed in the Union.

Minimal reproduceable example: https://play.strawberry.rocks/?gist=f7d88898d127e65b12140fdd763f9ef2

## Existing Code
The below check is logically incorrect as `possible_type.is_implemented_by(root)` might be True for a similar object that is not the correct type.

```python
# Iterate over all of our known types and find the first concrete type that
# implements the type
for possible_concrete_type in type_map.values():
    possible_type = possible_concrete_type.definition
    if not isinstance(possible_type, TypeDefinition):
        continue
    if possible_type.is_implemented_by(root):
        return_type = possible_concrete_type.implementation
        break
else:
    return_type = None
```

## Example Fix
Here we check the types explicitly listed in the Union first, and then fallback to checking all types.

```python
# Iterate over the given types in the union and check if any provide a
# concrete type that implements the type.
for gql_object_type in type_.types:
    type_name = gql_object_type.name
    possible_concrete_type = type_map.get(type_name)
    if possible_concrete_type:
        possible_type = possible_concrete_type.definition
        if not isinstance(possible_type, TypeDefinition):
            continue
        if possible_type.is_implemented_by(root):
            return_type = possible_concrete_type.implementation
            break
else:
    return_type = None

# Otherwise, iterate over all of our known types and find the first concrete
# type that implements the type.
if return_type is None:
    for possible_concrete_type in type_map.values():
        possible_type = possible_concrete_type.definition
        if not isinstance(possible_type, TypeDefinition):
            continue
        if possible_type.is_implemented_by(root):
            return_type = possible_concrete_type.implementation
            break
```

## System Information

 - Operating system: N/A
 - Strawberry version (if applicable): 0.119.1


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `strawberry/union.py`
Content:
```
1 import itertools
2 from typing import (
3     TYPE_CHECKING,
4     Any,
5     List,
6     Mapping,
7     NoReturn,
8     Optional,
9     Tuple,
10     Type,
11     TypeVar,
12     Union,
13     cast,
14 )
15 
16 from graphql import (
17     GraphQLAbstractType,
18     GraphQLNamedType,
19     GraphQLResolveInfo,
20     GraphQLType,
21     GraphQLTypeResolver,
22     GraphQLUnionType,
23 )
24 
25 from strawberry.annotation import StrawberryAnnotation
26 from strawberry.exceptions import (
27     InvalidUnionType,
28     UnallowedReturnTypeForUnion,
29     WrongReturnTypeForUnion,
30 )
31 from strawberry.type import StrawberryOptional, StrawberryType
32 
33 
34 if TYPE_CHECKING:
35     from strawberry.schema.types.concrete_type import TypeMap
36     from strawberry.types.types import TypeDefinition
37 
38 
39 class StrawberryUnion(StrawberryType):
40     def __init__(
41         self,
42         name: Optional[str] = None,
43         type_annotations: Tuple["StrawberryAnnotation", ...] = tuple(),
44         description: Optional[str] = None,
45     ):
46         self.graphql_name = name
47         self.type_annotations = type_annotations
48         self.description = description
49 
50     def __eq__(self, other: object) -> bool:
51         if isinstance(other, StrawberryType):
52             if isinstance(other, StrawberryUnion):
53                 return (
54                     self.graphql_name == other.graphql_name
55                     and self.type_annotations == other.type_annotations
56                     and self.description == other.description
57                 )
58             return False
59 
60         return super().__eq__(other)
61 
62     def __hash__(self) -> int:
63         # TODO: Is this a bad idea? __eq__ objects are supposed to have the same hash
64         return id(self)
65 
66     def __or__(self, other: Union[StrawberryType, type]) -> StrawberryType:
67         if other is None:
68             # Return the correct notation when using `StrawberryUnion | None`.
69             return StrawberryOptional(of_type=self)
70 
71         # Raise an error in any other case.
72         # There is Work in progress to deal with more merging cases, see:
73         # https://github.com/strawberry-graphql/strawberry/pull/1455
74         raise InvalidUnionType(other)
75 
76     @property
77     def types(self) -> Tuple[StrawberryType, ...]:
78         return tuple(
79             cast(StrawberryType, annotation.resolve())
80             for annotation in self.type_annotations
81         )
82 
83     @property
84     def type_params(self) -> List[TypeVar]:
85         def _get_type_params(type_: StrawberryType):
86             if hasattr(type_, "_type_definition"):
87                 parameters = getattr(type_, "__parameters__", None)
88 
89                 return list(parameters) if parameters else []
90 
91             return type_.type_params
92 
93         # TODO: check if order is important:
94         # https://github.com/strawberry-graphql/strawberry/issues/445
95         return list(
96             set(itertools.chain(*(_get_type_params(type_) for type_ in self.types)))
97         )
98 
99     @property
100     def is_generic(self) -> bool:
101         return len(self.type_params) > 0
102 
103     def copy_with(
104         self, type_var_map: Mapping[TypeVar, Union[StrawberryType, type]]
105     ) -> StrawberryType:
106         if not self.is_generic:
107             return self
108 
109         new_types = []
110         for type_ in self.types:
111             new_type: Union[StrawberryType, type]
112 
113             if hasattr(type_, "_type_definition"):
114                 type_definition: TypeDefinition = type_._type_definition  # type: ignore
115 
116                 if type_definition.is_generic:
117                     new_type = type_definition.copy_with(type_var_map)
118             if isinstance(type_, StrawberryType) and type_.is_generic:
119                 new_type = type_.copy_with(type_var_map)
120             else:
121                 new_type = type_
122 
123             new_types.append(new_type)
124 
125         return StrawberryUnion(
126             type_annotations=tuple(map(StrawberryAnnotation, new_types)),
127             description=self.description,
128         )
129 
130     def __call__(self, *_args, **_kwargs) -> NoReturn:
131         """Do not use.
132 
133         Used to bypass
134         https://github.com/python/cpython/blob/5efb1a77e75648012f8b52960c8637fc296a5c6d/Lib/typing.py#L148-L149
135         """
136         raise ValueError("Cannot use union type directly")
137 
138     def get_type_resolver(self, type_map: "TypeMap") -> GraphQLTypeResolver:
139         def _resolve_union_type(
140             root: Any, info: GraphQLResolveInfo, type_: GraphQLAbstractType
141         ) -> str:
142             assert isinstance(type_, GraphQLUnionType)
143 
144             from strawberry.types.types import TypeDefinition
145 
146             # If the type given is not an Object type, try resolving using `is_type_of`
147             # defined on the union's inner types
148             if not hasattr(root, "_type_definition"):
149                 for inner_type in type_.types:
150                     if inner_type.is_type_of is not None and inner_type.is_type_of(
151                         root, info
152                     ):
153                         return inner_type.name
154 
155                 # Couldn't resolve using `is_type_of``
156                 raise WrongReturnTypeForUnion(info.field_name, str(type(root)))
157 
158             return_type: Optional[GraphQLType]
159 
160             # Iterate over all of our known types and find the first concrete type that
161             # implements the type
162             for possible_concrete_type in type_map.values():
163                 possible_type = possible_concrete_type.definition
164                 if not isinstance(possible_type, TypeDefinition):
165                     continue
166                 if possible_type.is_implemented_by(root):
167                     return_type = possible_concrete_type.implementation
168                     break
169             else:
170                 return_type = None
171 
172             # Make sure the found type is expected by the Union
173             if return_type is None or return_type not in type_.types:
174                 raise UnallowedReturnTypeForUnion(
175                     info.field_name, str(type(root)), set(type_.types)
176                 )
177 
178             # Return the name of the type. Returning the actual type is now deprecated
179             if isinstance(return_type, GraphQLNamedType):
180                 # TODO: Can return_type ever _not_ be a GraphQLNamedType?
181                 return return_type.name
182             else:
183                 # todo: check if this is correct
184                 return return_type.__name__  # type: ignore
185 
186         return _resolve_union_type
187 
188 
189 Types = TypeVar("Types", bound=Type)
190 
191 
192 # We return a Union type here in order to allow to use the union type as type
193 # annotation.
194 # For the `types` argument we'd ideally use a TypeVarTuple, but that's not
195 # yet supported in any python implementation (or in typing_extensions).
196 # See https://www.python.org/dev/peps/pep-0646/ for more information
197 def union(
198     name: str, types: Tuple[Types, ...], *, description: str = None
199 ) -> Union[Types]:
200     """Creates a new named Union type.
201 
202     Example usages:
203 
204     >>> @strawberry.type
205     ... class A: ...
206     >>> @strawberry.type
207     ... class B: ...
208     >>> strawberry.union("Name", (A, Optional[B]))
209     """
210 
211     # Validate types
212     if len(types) == 0:
213         raise TypeError("No types passed to `union`")
214 
215     for _type in types:
216         if not isinstance(_type, TypeVar) and not hasattr(_type, "_type_definition"):
217             raise InvalidUnionType(
218                 f"Type `{_type.__name__}` cannot be used in a GraphQL Union"
219             )
220 
221     union_definition = StrawberryUnion(
222         name=name,
223         type_annotations=tuple(StrawberryAnnotation(type_) for type_ in types),
224         description=description,
225     )
226 
227     return union_definition  # type: ignore
228
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/strawberry/union.py b/strawberry/union.py
--- a/strawberry/union.py
+++ b/strawberry/union.py
@@ -1,4 +1,5 @@
 import itertools
+from itertools import chain
 from typing import (
     TYPE_CHECKING,
     Any,
@@ -152,14 +153,20 @@
                     ):
                         return inner_type.name
 
-                # Couldn't resolve using `is_type_of``
+                # Couldn't resolve using `is_type_of`
                 raise WrongReturnTypeForUnion(info.field_name, str(type(root)))
 
             return_type: Optional[GraphQLType]
 
-            # Iterate over all of our known types and find the first concrete type that
-            # implements the type
-            for possible_concrete_type in type_map.values():
+            # Iterate over all of our known types and find the first concrete
+            # type that implements the type. We prioritise checking types named in the
+            # Union in case a nested generic object matches against more than one type.
+            concrete_types_for_union = (type_map[x.name] for x in type_.types)
+
+            # TODO: do we still need to iterate over all types in `type_map`?
+            for possible_concrete_type in chain(
+                concrete_types_for_union, type_map.values()
+            ):
                 possible_type = possible_concrete_type.definition
                 if not isinstance(possible_type, TypeDefinition):
                     continue

{"golden_diff": "diff --git a/strawberry/union.py b/strawberry/union.py\n--- a/strawberry/union.py\n+++ b/strawberry/union.py\n@@ -1,4 +1,5 @@\n import itertools\n+from itertools import chain\n from typing import (\n     TYPE_CHECKING,\n     Any,\n@@ -152,14 +153,20 @@\n                     ):\n                         return inner_type.name\n \n-                # Couldn't resolve using `is_type_of``\n+                # Couldn't resolve using `is_type_of`\n                 raise WrongReturnTypeForUnion(info.field_name, str(type(root)))\n \n             return_type: Optional[GraphQLType]\n \n-            # Iterate over all of our known types and find the first concrete type that\n-            # implements the type\n-            for possible_concrete_type in type_map.values():\n+            # Iterate over all of our known types and find the first concrete\n+            # type that implements the type. We prioritise checking types named in the\n+            # Union in case a nested generic object matches against more than one type.\n+            concrete_types_for_union = (type_map[x.name] for x in type_.types)\n+\n+            # TODO: do we still need to iterate over all types in `type_map`?\n+            for possible_concrete_type in chain(\n+                concrete_types_for_union, type_map.values()\n+            ):\n                 possible_type = possible_concrete_type.definition\n                 if not isinstance(possible_type, TypeDefinition):\n                     continue\n", "issue": "StrawberryUnion's type resolver does not prioritise types in the Union\n## Describe the Bug\r\n\r\nStrawberryUnion's type resolver iterates over all known types and returns the first type which successfully implements the returned object from the resolver.\r\n\r\nDue to limitations of `TypeDefinition.is_implemented_by` when checking generic fields, this check might succeed prematurely for an object with the same top level structure.\r\n\r\nIf the wrong type is selected a `UnallowedReturnTypeForUnion` error is raised shortly afterwards.\r\n\r\nPotentially it might be possible to improve `TypeDefinition.is_implemented_by` check for generic fields but that is beyond the scope of this issue.\r\n\r\nThis issue suggests a simple fix of first checking the types given in the union before falling back to checking all types. This might also be a small performance improvement if it is true that in most cases the correct type is listed in the Union.\r\n\r\nMinimal reproduceable example: https://play.strawberry.rocks/?gist=f7d88898d127e65b12140fdd763f9ef2\r\n\r\n## Existing Code\r\nThe below check is logically incorrect as `possible_type.is_implemented_by(root)` might be True for a similar object that is not the correct type.\r\n\r\n```python\r\n# Iterate over all of our known types and find the first concrete type that\r\n# implements the type\r\nfor possible_concrete_type in type_map.values():\r\n    possible_type = possible_concrete_type.definition\r\n    if not isinstance(possible_type, TypeDefinition):\r\n        continue\r\n    if possible_type.is_implemented_by(root):\r\n        return_type = possible_concrete_type.implementation\r\n        break\r\nelse:\r\n    return_type = None\r\n```\r\n\r\n## Example Fix\r\nHere we check the types explicitly listed in the Union first, and then fallback to checking all types.\r\n\r\n```python\r\n# Iterate over the given types in the union and check if any provide a\r\n# concrete type that implements the type.\r\nfor gql_object_type in type_.types:\r\n    type_name = gql_object_type.name\r\n    possible_concrete_type = type_map.get(type_name)\r\n    if possible_concrete_type:\r\n        possible_type = possible_concrete_type.definition\r\n        if not isinstance(possible_type, TypeDefinition):\r\n            continue\r\n        if possible_type.is_implemented_by(root):\r\n            return_type = possible_concrete_type.implementation\r\n            break\r\nelse:\r\n    return_type = None\r\n\r\n# Otherwise, iterate over all of our known types and find the first concrete\r\n# type that implements the type.\r\nif return_type is None:\r\n    for possible_concrete_type in type_map.values():\r\n        possible_type = possible_concrete_type.definition\r\n        if not isinstance(possible_type, TypeDefinition):\r\n            continue\r\n        if possible_type.is_implemented_by(root):\r\n            return_type = possible_concrete_type.implementation\r\n            break\r\n```\r\n\r\n## System Information\r\n\r\n - Operating system: N/A\r\n - Strawberry version (if applicable): 0.119.1\r\n\n", "before_files": [{"content": "import itertools\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    List,\n    Mapping,\n    NoReturn,\n    Optional,\n    Tuple,\n    Type,\n    TypeVar,\n    Union,\n    cast,\n)\n\nfrom graphql import (\n    GraphQLAbstractType,\n    GraphQLNamedType,\n    GraphQLResolveInfo,\n    GraphQLType,\n    GraphQLTypeResolver,\n    GraphQLUnionType,\n)\n\nfrom strawberry.annotation import StrawberryAnnotation\nfrom strawberry.exceptions import (\n    InvalidUnionType,\n    UnallowedReturnTypeForUnion,\n    WrongReturnTypeForUnion,\n)\nfrom strawberry.type import StrawberryOptional, StrawberryType\n\n\nif TYPE_CHECKING:\n    from strawberry.schema.types.concrete_type import TypeMap\n    from strawberry.types.types import TypeDefinition\n\n\nclass StrawberryUnion(StrawberryType):\n    def __init__(\n        self,\n        name: Optional[str] = None,\n        type_annotations: Tuple[\"StrawberryAnnotation\", ...] = tuple(),\n        description: Optional[str] = None,\n    ):\n        self.graphql_name = name\n        self.type_annotations = type_annotations\n        self.description = description\n\n    def __eq__(self, other: object) -> bool:\n        if isinstance(other, StrawberryType):\n            if isinstance(other, StrawberryUnion):\n                return (\n                    self.graphql_name == other.graphql_name\n                    and self.type_annotations == other.type_annotations\n                    and self.description == other.description\n                )\n            return False\n\n        return super().__eq__(other)\n\n    def __hash__(self) -> int:\n        # TODO: Is this a bad idea? __eq__ objects are supposed to have the same hash\n        return id(self)\n\n    def __or__(self, other: Union[StrawberryType, type]) -> StrawberryType:\n        if other is None:\n            # Return the correct notation when using `StrawberryUnion | None`.\n            return StrawberryOptional(of_type=self)\n\n        # Raise an error in any other case.\n        # There is Work in progress to deal with more merging cases, see:\n        # https://github.com/strawberry-graphql/strawberry/pull/1455\n        raise InvalidUnionType(other)\n\n    @property\n    def types(self) -> Tuple[StrawberryType, ...]:\n        return tuple(\n            cast(StrawberryType, annotation.resolve())\n            for annotation in self.type_annotations\n        )\n\n    @property\n    def type_params(self) -> List[TypeVar]:\n        def _get_type_params(type_: StrawberryType):\n            if hasattr(type_, \"_type_definition\"):\n                parameters = getattr(type_, \"__parameters__\", None)\n\n                return list(parameters) if parameters else []\n\n            return type_.type_params\n\n        # TODO: check if order is important:\n        # https://github.com/strawberry-graphql/strawberry/issues/445\n        return list(\n            set(itertools.chain(*(_get_type_params(type_) for type_ in self.types)))\n        )\n\n    @property\n    def is_generic(self) -> bool:\n        return len(self.type_params) > 0\n\n    def copy_with(\n        self, type_var_map: Mapping[TypeVar, Union[StrawberryType, type]]\n    ) -> StrawberryType:\n        if not self.is_generic:\n            return self\n\n        new_types = []\n        for type_ in self.types:\n            new_type: Union[StrawberryType, type]\n\n            if hasattr(type_, \"_type_definition\"):\n                type_definition: TypeDefinition = type_._type_definition  # type: ignore\n\n                if type_definition.is_generic:\n                    new_type = type_definition.copy_with(type_var_map)\n            if isinstance(type_, StrawberryType) and type_.is_generic:\n                new_type = type_.copy_with(type_var_map)\n            else:\n                new_type = type_\n\n            new_types.append(new_type)\n\n        return StrawberryUnion(\n            type_annotations=tuple(map(StrawberryAnnotation, new_types)),\n            description=self.description,\n        )\n\n    def __call__(self, *_args, **_kwargs) -> NoReturn:\n        \"\"\"Do not use.\n\n        Used to bypass\n        https://github.com/python/cpython/blob/5efb1a77e75648012f8b52960c8637fc296a5c6d/Lib/typing.py#L148-L149\n        \"\"\"\n        raise ValueError(\"Cannot use union type directly\")\n\n    def get_type_resolver(self, type_map: \"TypeMap\") -> GraphQLTypeResolver:\n        def _resolve_union_type(\n            root: Any, info: GraphQLResolveInfo, type_: GraphQLAbstractType\n        ) -> str:\n            assert isinstance(type_, GraphQLUnionType)\n\n            from strawberry.types.types import TypeDefinition\n\n            # If the type given is not an Object type, try resolving using `is_type_of`\n            # defined on the union's inner types\n            if not hasattr(root, \"_type_definition\"):\n                for inner_type in type_.types:\n                    if inner_type.is_type_of is not None and inner_type.is_type_of(\n                        root, info\n                    ):\n                        return inner_type.name\n\n                # Couldn't resolve using `is_type_of``\n                raise WrongReturnTypeForUnion(info.field_name, str(type(root)))\n\n            return_type: Optional[GraphQLType]\n\n            # Iterate over all of our known types and find the first concrete type that\n            # implements the type\n            for possible_concrete_type in type_map.values():\n                possible_type = possible_concrete_type.definition\n                if not isinstance(possible_type, TypeDefinition):\n                    continue\n                if possible_type.is_implemented_by(root):\n                    return_type = possible_concrete_type.implementation\n                    break\n            else:\n                return_type = None\n\n            # Make sure the found type is expected by the Union\n            if return_type is None or return_type not in type_.types:\n                raise UnallowedReturnTypeForUnion(\n                    info.field_name, str(type(root)), set(type_.types)\n                )\n\n            # Return the name of the type. Returning the actual type is now deprecated\n            if isinstance(return_type, GraphQLNamedType):\n                # TODO: Can return_type ever _not_ be a GraphQLNamedType?\n                return return_type.name\n            else:\n                # todo: check if this is correct\n                return return_type.__name__  # type: ignore\n\n        return _resolve_union_type\n\n\nTypes = TypeVar(\"Types\", bound=Type)\n\n\n# We return a Union type here in order to allow to use the union type as type\n# annotation.\n# For the `types` argument we'd ideally use a TypeVarTuple, but that's not\n# yet supported in any python implementation (or in typing_extensions).\n# See https://www.python.org/dev/peps/pep-0646/ for more information\ndef union(\n    name: str, types: Tuple[Types, ...], *, description: str = None\n) -> Union[Types]:\n    \"\"\"Creates a new named Union type.\n\n    Example usages:\n\n    >>> @strawberry.type\n    ... class A: ...\n    >>> @strawberry.type\n    ... class B: ...\n    >>> strawberry.union(\"Name\", (A, Optional[B]))\n    \"\"\"\n\n    # Validate types\n    if len(types) == 0:\n        raise TypeError(\"No types passed to `union`\")\n\n    for _type in types:\n        if not isinstance(_type, TypeVar) and not hasattr(_type, \"_type_definition\"):\n            raise InvalidUnionType(\n                f\"Type `{_type.__name__}` cannot be used in a GraphQL Union\"\n            )\n\n    union_definition = StrawberryUnion(\n        name=name,\n        type_annotations=tuple(StrawberryAnnotation(type_) for type_ in types),\n        description=description,\n    )\n\n    return union_definition  # type: ignore\n", "path": "strawberry/union.py"}], "after_files": [{"content": "import itertools\nfrom itertools import chain\nfrom typing import (\n    TYPE_CHECKING,\n    Any,\n    List,\n    Mapping,\n    NoReturn,\n    Optional,\n    Tuple,\n    Type,\n    TypeVar,\n    Union,\n    cast,\n)\n\nfrom graphql import (\n    GraphQLAbstractType,\n    GraphQLNamedType,\n    GraphQLResolveInfo,\n    GraphQLType,\n    GraphQLTypeResolver,\n    GraphQLUnionType,\n)\n\nfrom strawberry.annotation import StrawberryAnnotation\nfrom strawberry.exceptions import (\n    InvalidUnionType,\n    UnallowedReturnTypeForUnion,\n    WrongReturnTypeForUnion,\n)\nfrom strawberry.type import StrawberryOptional, StrawberryType\n\n\nif TYPE_CHECKING:\n    from strawberry.schema.types.concrete_type import TypeMap\n    from strawberry.types.types import TypeDefinition\n\n\nclass StrawberryUnion(StrawberryType):\n    def __init__(\n        self,\n        name: Optional[str] = None,\n        type_annotations: Tuple[\"StrawberryAnnotation\", ...] = tuple(),\n        description: Optional[str] = None,\n    ):\n        self.graphql_name = name\n        self.type_annotations = type_annotations\n        self.description = description\n\n    def __eq__(self, other: object) -> bool:\n        if isinstance(other, StrawberryType):\n            if isinstance(other, StrawberryUnion):\n                return (\n                    self.graphql_name == other.graphql_name\n                    and self.type_annotations == other.type_annotations\n                    and self.description == other.description\n                )\n            return False\n\n        return super().__eq__(other)\n\n    def __hash__(self) -> int:\n        # TODO: Is this a bad idea? __eq__ objects are supposed to have the same hash\n        return id(self)\n\n    def __or__(self, other: Union[StrawberryType, type]) -> StrawberryType:\n        if other is None:\n            # Return the correct notation when using `StrawberryUnion | None`.\n            return StrawberryOptional(of_type=self)\n\n        # Raise an error in any other case.\n        # There is Work in progress to deal with more merging cases, see:\n        # https://github.com/strawberry-graphql/strawberry/pull/1455\n        raise InvalidUnionType(other)\n\n    @property\n    def types(self) -> Tuple[StrawberryType, ...]:\n        return tuple(\n            cast(StrawberryType, annotation.resolve())\n            for annotation in self.type_annotations\n        )\n\n    @property\n    def type_params(self) -> List[TypeVar]:\n        def _get_type_params(type_: StrawberryType):\n            if hasattr(type_, \"_type_definition\"):\n                parameters = getattr(type_, \"__parameters__\", None)\n\n                return list(parameters) if parameters else []\n\n            return type_.type_params\n\n        # TODO: check if order is important:\n        # https://github.com/strawberry-graphql/strawberry/issues/445\n        return list(\n            set(itertools.chain(*(_get_type_params(type_) for type_ in self.types)))\n        )\n\n    @property\n    def is_generic(self) -> bool:\n        return len(self.type_params) > 0\n\n    def copy_with(\n        self, type_var_map: Mapping[TypeVar, Union[StrawberryType, type]]\n    ) -> StrawberryType:\n        if not self.is_generic:\n            return self\n\n        new_types = []\n        for type_ in self.types:\n            new_type: Union[StrawberryType, type]\n\n            if hasattr(type_, \"_type_definition\"):\n                type_definition: TypeDefinition = type_._type_definition  # type: ignore\n\n                if type_definition.is_generic:\n                    new_type = type_definition.copy_with(type_var_map)\n            if isinstance(type_, StrawberryType) and type_.is_generic:\n                new_type = type_.copy_with(type_var_map)\n            else:\n                new_type = type_\n\n            new_types.append(new_type)\n\n        return StrawberryUnion(\n            type_annotations=tuple(map(StrawberryAnnotation, new_types)),\n            description=self.description,\n        )\n\n    def __call__(self, *_args, **_kwargs) -> NoReturn:\n        \"\"\"Do not use.\n\n        Used to bypass\n        https://github.com/python/cpython/blob/5efb1a77e75648012f8b52960c8637fc296a5c6d/Lib/typing.py#L148-L149\n        \"\"\"\n        raise ValueError(\"Cannot use union type directly\")\n\n    def get_type_resolver(self, type_map: \"TypeMap\") -> GraphQLTypeResolver:\n        def _resolve_union_type(\n            root: Any, info: GraphQLResolveInfo, type_: GraphQLAbstractType\n        ) -> str:\n            assert isinstance(type_, GraphQLUnionType)\n\n            from strawberry.types.types import TypeDefinition\n\n            # If the type given is not an Object type, try resolving using `is_type_of`\n            # defined on the union's inner types\n            if not hasattr(root, \"_type_definition\"):\n                for inner_type in type_.types:\n                    if inner_type.is_type_of is not None and inner_type.is_type_of(\n                        root, info\n                    ):\n                        return inner_type.name\n\n                # Couldn't resolve using `is_type_of`\n                raise WrongReturnTypeForUnion(info.field_name, str(type(root)))\n\n            return_type: Optional[GraphQLType]\n\n            # Iterate over all of our known types and find the first concrete\n            # type that implements the type. We prioritise checking types named in the\n            # Union in case a nested generic object matches against more than one type.\n            concrete_types_for_union = (type_map[x.name] for x in type_.types)\n\n            # TODO: do we still need to iterate over all types in `type_map`?\n            for possible_concrete_type in chain(\n                concrete_types_for_union, type_map.values()\n            ):\n                possible_type = possible_concrete_type.definition\n                if not isinstance(possible_type, TypeDefinition):\n                    continue\n                if possible_type.is_implemented_by(root):\n                    return_type = possible_concrete_type.implementation\n                    break\n            else:\n                return_type = None\n\n            # Make sure the found type is expected by the Union\n            if return_type is None or return_type not in type_.types:\n                raise UnallowedReturnTypeForUnion(\n                    info.field_name, str(type(root)), set(type_.types)\n                )\n\n            # Return the name of the type. Returning the actual type is now deprecated\n            if isinstance(return_type, GraphQLNamedType):\n                # TODO: Can return_type ever _not_ be a GraphQLNamedType?\n                return return_type.name\n            else:\n                # todo: check if this is correct\n                return return_type.__name__  # type: ignore\n\n        return _resolve_union_type\n\n\nTypes = TypeVar(\"Types\", bound=Type)\n\n\n# We return a Union type here in order to allow to use the union type as type\n# annotation.\n# For the `types` argument we'd ideally use a TypeVarTuple, but that's not\n# yet supported in any python implementation (or in typing_extensions).\n# See https://www.python.org/dev/peps/pep-0646/ for more information\ndef union(\n    name: str, types: Tuple[Types, ...], *, description: str = None\n) -> Union[Types]:\n    \"\"\"Creates a new named Union type.\n\n    Example usages:\n\n    >>> @strawberry.type\n    ... class A: ...\n    >>> @strawberry.type\n    ... class B: ...\n    >>> strawberry.union(\"Name\", (A, Optional[B]))\n    \"\"\"\n\n    # Validate types\n    if len(types) == 0:\n        raise TypeError(\"No types passed to `union`\")\n\n    for _type in types:\n        if not isinstance(_type, TypeVar) and not hasattr(_type, \"_type_definition\"):\n            raise InvalidUnionType(\n                f\"Type `{_type.__name__}` cannot be used in a GraphQL Union\"\n            )\n\n    union_definition = StrawberryUnion(\n        name=name,\n        type_annotations=tuple(StrawberryAnnotation(type_) for type_ in types),\n        description=description,\n    )\n\n    return union_definition  # type: ignore\n", "path": "strawberry/union.py"}]}