Repository: strawberry-graphql/strawberry

Issue:
`strawberry.directive` type hints appear to be incorrect.
<!-- Provide a general summary of the bug in the title above. -->

<!--- This template is entirely optional and can be removed, but is here to help both you and us. -->
<!--- Anything on lines wrapped in comments like these will not show up in the final text. -->

## Describe the Bug

A function like:

```py
@strawberry.directive(
    locations=[DirectiveLocation.FRAGMENT_DEFINITION],
    description="Do nothing, but add a base class for query generation in the python client.",
)
def identity(value: str, a: str, b: str | None = None) -> str:
    """Do nothing, but add a directive so that the python client can use this to add metadata."""
    return value

reveal_type(identity)
```

`mypy` will say that the revealed type here is `builtins.str`.  Looking at the code for `directive`, it appears that the type hints imply that the decorated object is going to return the same type that the function returns (in this case, the return type of `identity`)


## System Information

 - Operating system:
 - Strawberry version (if applicable):

## Additional Context

<!-- Add any other relevant information about the problem here. -->


<!-- POLAR PLEDGE BADGE START -->
## Upvote & Fund

- We're using [Polar.sh](https://polar.sh/strawberry-graphql) so you can upvote and help fund this issue.
- We receive the funding once the issue is completed & confirmed by you.
- Thank you in advance for helping prioritize & fund our backlog.

<a href="https://polar.sh/strawberry-graphql/strawberry/issues/2846">
<picture>
  <source media="(prefers-color-scheme: dark)" srcset="https://polar.sh/api/github/strawberry-graphql/strawberry/issues/2846/pledge.svg?darkmode=1">
  <img alt="Fund with Polar" src="https://polar.sh/api/github/strawberry-graphql/strawberry/issues/2846/pledge.svg">
</picture>
</a>
<!-- POLAR PLEDGE BADGE END -->



Discussion:

Resolving patch:
diff --git a/strawberry/directive.py b/strawberry/directive.py
--- a/strawberry/directive.py
+++ b/strawberry/directive.py
@@ -1,7 +1,7 @@
 from __future__ import annotations
 
 import dataclasses
-from typing import TYPE_CHECKING, Any, Callable, List, Optional, TypeVar
+from typing import TYPE_CHECKING, Any, Callable, Generic, List, Optional, TypeVar
 from typing_extensions import Annotated
 
 from graphql import DirectiveLocation
@@ -57,10 +57,10 @@ def value_parameter(self) -> Optional[inspect.Parameter]:
 
 
 @dataclasses.dataclass
-class StrawberryDirective:
+class StrawberryDirective(Generic[T]):
     python_name: str
     graphql_name: Optional[str]
-    resolver: StrawberryDirectiveResolver
+    resolver: StrawberryDirectiveResolver[T]
     locations: List[DirectiveLocation]
     description: Optional[str] = None
 
@@ -74,9 +74,9 @@ def directive(
     locations: List[DirectiveLocation],
     description: Optional[str] = None,
     name: Optional[str] = None,
-) -> Callable[[Callable[..., T]], T]:
-    def _wrap(f: Callable[..., T]) -> T:
-        return StrawberryDirective(  # type: ignore
+) -> Callable[[Callable[..., T]], StrawberryDirective[T]]:
+    def _wrap(f: Callable[..., T]) -> StrawberryDirective[T]:
+        return StrawberryDirective(
             python_name=f.__name__,
             graphql_name=name,
             locations=locations,


Test patch:
diff --git a/tests/mypy/test_directives.yml b/tests/mypy/test_directives.yml
new file mode 100644
--- /dev/null
+++ b/tests/mypy/test_directives.yml
@@ -0,0 +1,20 @@
+- case: test_directive_decorator
+  main: |
+    import strawberry
+    from strawberry.directive import DirectiveLocation
+
+    @strawberry.directive(
+        locations=[DirectiveLocation.FRAGMENT_DEFINITION],
+        description="description.",
+    )
+    def make_int(value: str) -> int:
+        """description."""
+        try:
+            return int(value)
+        except ValueError:
+            return 0
+
+    reveal_type(make_int)
+
+  out: |
+    main:15: note: Revealed type is "strawberry.directive.StrawberryDirective[builtins.int]"
diff --git a/tests/pyright/test_directives.py b/tests/pyright/test_directives.py
new file mode 100644
--- /dev/null
+++ b/tests/pyright/test_directives.py
@@ -0,0 +1,34 @@
+from .utils import Result, requires_pyright, run_pyright, skip_on_windows
+
+pytestmark = [skip_on_windows, requires_pyright]
+
+
+CODE = """
+import strawberry
+from strawberry.directive import DirectiveLocation
+
+@strawberry.directive(
+    locations=[DirectiveLocation.FRAGMENT_DEFINITION],
+    description="description.",
+)
+def make_int(value: str) -> int:
+    '''description.'''
+    try:
+        return int(value)
+    except ValueError:
+        return 0
+
+reveal_type(make_int)
+"""
+
+
+def test_pyright():
+    results = run_pyright(CODE)
+    assert results == [
+        Result(
+            type="information",
+            message='Type of "make_int" is "StrawberryDirective[int]"',
+            line=16,
+            column=13,
+        ),
+    ]