You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/interfaces.md
+104-2Lines changed: 104 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -14,6 +14,8 @@ TypeScript has first class support for interfaces. Unfortunately, they only exis
14
14
15
15
Luckily, we can use an abstract class for this purpose. It behaves almost like an interface - it can't be "newed" but it can be implemented by the class - and it just won't prevent developers from implementing a method or initializing a field. So, as long as we treat it like an interface, we can safely use it.
16
16
17
+
### Defining interface type
18
+
17
19
How do we create a GraphQL interface definition? We create an abstract class and decorate it with the `@InterfaceType()` decorator. The rest is exactly the same as with object types: we use the `@Field` decorator to declare the shape of the type:
18
20
19
21
```typescript
@@ -30,7 +32,7 @@ abstract class IPerson {
30
32
}
31
33
```
32
34
33
-
We can then we use this "interface" in the object type class definition:
35
+
We can then use this "interface" in the object type class definition:
34
36
35
37
```typescript
36
38
@ObjectType({ implements: IPerson })
@@ -43,7 +45,107 @@ class Person implements IPerson {
43
45
44
46
The only difference is that we have to let TypeGraphQL know that this `ObjectType` is implementing the `InterfaceType`. We do this by passing the param `({ implements: IPerson })` to the decorator. If we implement multiple interfaces, we pass an array of interfaces like so: `({ implements: [IPerson, IAnimal, IMachine] })`.
45
47
46
-
We can also omit the decorators since the GraphQL types will be copied from the interface definition - this way we won't have to maintain two definitions and solely rely on TypeScript type checking for correct interface implementation.
48
+
It is also allowed to omit the decorators since the GraphQL types will be copied from the interface definition - this way we won't have to maintain two definitions and solely rely on TypeScript type checking for correct interface implementation.
49
+
50
+
We can extend the base interface type abstract class as well because all the fields are inherited and emitted in schema:
51
+
52
+
```typescript
53
+
@ObjectType({ implements: IPerson })
54
+
classPersonextendsIPerson {
55
+
@Field()
56
+
hasKids:boolean;
57
+
}
58
+
```
59
+
60
+
### Resolvers and arguments
61
+
62
+
What's more, we can define resolvers for the interface fields, using the same syntax we would use when defining one for our object type:
63
+
64
+
```typescript
65
+
@InterfaceType()
66
+
abstractclassIPerson {
67
+
@Field()
68
+
firstName:string;
69
+
70
+
@Field()
71
+
lastName:string;
72
+
73
+
@Field()
74
+
fullName():string {
75
+
return`${this.firstName} ${this.lastName}`;
76
+
}
77
+
}
78
+
```
79
+
80
+
They're inherited by all the object types that implements this interface type but does not provide their own resolver implementation for those fields.
81
+
82
+
Additionally, if we want to declare that the interface accepts some arguments, e.g.:
83
+
84
+
```graphql
85
+
interfaceIPerson {
86
+
avatar(size: Int!): String!
87
+
}
88
+
```
89
+
90
+
Wecanjustuse `@Arg` or `@Args` decoratorsasusual:
91
+
92
+
```typescript
93
+
@InterfaceType()
94
+
abstractclassIPerson {
95
+
@Field()
96
+
avatar(@Arg("size") size: number): string {
97
+
return `http://i.pravatar.cc/${size}`;
98
+
}
99
+
}
100
+
```
101
+
102
+
Unfortunately, TypeScript doesn't allow using decorators on abstract methods.
103
+
So if we don't want to provide implementation for that field resolver, only to enforce some signature (args and return type), we have to throw an error inside the body:
104
+
105
+
```typescript
106
+
@InterfaceType()
107
+
abstractclassIPerson {
108
+
@Field()
109
+
avatar(@Arg("size") size:number):string {
110
+
thrownewError("Method not implemented!");
111
+
}
112
+
}
113
+
```
114
+
115
+
And then we need to extend the interface class and override the method by providing its body - it is required for all object types that implements that interface type:
116
+
117
+
```typescript
118
+
@ObjectType({ implements: IPerson })
119
+
classPersonextendsIPerson {
120
+
avatar(size:number):string {
121
+
return`http://i.pravatar.cc/${size}`;
122
+
}
123
+
}
124
+
```
125
+
126
+
In order to extend the signature by providing additional arguments (like `format`), we need to redeclare the whole field signature:
0 commit comments