Skip to content

Commit de78270

Browse files
authored
Ensure object types across multiple schemas have __isTypeOf in Pick values (#319)
* Make sure __isTypeOf is in the picked properties * Update e2e tests * Add changeset
1 parent 741c9e1 commit de78270

File tree

25 files changed

+62
-43
lines changed

25 files changed

+62
-43
lines changed

.changeset/seven-camels-compete.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@eddeee888/gcg-typescript-resolver-files': patch
3+
---
4+
5+
Ensure \_\_isTypeOf is in the picked properties so users can choose this way to handle abstract type should they choose
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import type { TopicResolvers } from './../../types.generated';
2-
export const Topic: Pick<TopicResolvers, 'bookStore_for_topic'> = {
3-
/* Implement Topic resolver logic here */
4-
bookStore_for_topic: async (_parent, _arg, _ctx) => {
5-
/* Topic.bookStore_for_topic resolver is required because Topic.bookStore_for_topic exists but TopicMapper.bookStore_for_topic does not */
6-
},
7-
};
2+
export const Topic: Pick<TopicResolvers, 'bookStore_for_topic' | '__isTypeOf'> =
3+
{
4+
/* Implement Topic resolver logic here */
5+
bookStore_for_topic: async (_parent, _arg, _ctx) => {
6+
/* Topic.bookStore_for_topic resolver is required because Topic.bookStore_for_topic exists but TopicMapper.bookStore_for_topic does not */
7+
},
8+
};
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import type { UserResolvers } from './../../types.generated';
2-
export const User: Pick<UserResolvers, 'bookStore_4_user'> = {
2+
export const User: Pick<UserResolvers, 'bookStore_4_user' | '__isTypeOf'> = {
33
/* Implement User resolver logic here */
44
};

packages/typescript-resolver-files-e2e/src/test-extended-object-types/schema-base/topic/resolvers/Topic.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export const Topic: Pick<
1111
| 'id'
1212
| 'name'
1313
| 'url'
14+
| '__isTypeOf'
1415
> = {
1516
/* Implement Topic resolver logic here */
1617
extendedTopicFieldInDifferentFileAndSameModule1: async (

packages/typescript-resolver-files-e2e/src/test-extended-object-types/schema-base/user/resolvers/Topic.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ export const Topic: Pick<
55
| 'extendedTopicFieldInDifferentFileAndDifferentModule1'
66
| 'extendedTopicFieldInDifferentFileAndDifferentModule2'
77
| 'extendedTopicFieldInDifferentFileAndDifferentModule3'
8+
| '__isTypeOf'
89
> = {
910
/* Implement Topic resolver logic here */
1011
creator: ({ creator }, _arg, _ctx) => {

packages/typescript-resolver-files-e2e/src/test-extended-object-types/schema-base/user/resolvers/User.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export const User: Pick<
99
| 'avatar'
1010
| 'id'
1111
| 'name'
12+
| '__isTypeOf'
1213
> = {
1314
/* Implement User resolver logic here */
1415
};
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { ForeignTypeResolvers } from './../../types.generated';
22
export const ForeignType: Pick<
33
ForeignTypeResolvers,
4-
'id' | 'topics' | '__resolveReference'
4+
'id' | 'topics' | '__isTypeOf' | '__resolveReference'
55
> = {
66
/* Implement ForeignType resolver logic here */
77
};
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { ForeignTypeResolvers } from './../../types.generated';
22
export const ForeignType: Pick<
33
ForeignTypeResolvers,
4-
'users' | '__resolveReference'
4+
'users' | '__isTypeOf' | '__resolveReference'
55
> = {
66
/* Implement ForeignType resolver logic here */
77
};
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import type { SchoolResolvers } from './../../../types.generated';
2-
export const School: Pick<SchoolResolvers, 'courses'> = {
2+
export const School: Pick<SchoolResolvers, 'courses' | '__isTypeOf'> = {
33
/* Implement School resolver logic here */
44
};
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import type { SchoolResolvers } from './../../../types.generated';
2-
export const School: Pick<SchoolResolvers, 'demographics'> = {
2+
export const School: Pick<SchoolResolvers, 'demographics' | '__isTypeOf'> = {
33
/* Implement School resolver logic here */
44
};
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import type { SchoolResolvers } from './../../../types.generated';
2-
export const School: Pick<SchoolResolvers, 'id' | 'name'> = {
2+
export const School: Pick<SchoolResolvers, 'id' | 'name' | '__isTypeOf'> = {
33
/* Implement School resolver logic here */
44
};
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import type { StudentProfileResolvers } from './../../../types.generated';
2-
export const StudentProfile: Pick<StudentProfileResolvers, 'avatar'> = {
2+
export const StudentProfile: Pick<
3+
StudentProfileResolvers,
4+
'avatar' | '__isTypeOf'
5+
> = {
36
/* Implement StudentProfile resolver logic here */
47
};
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import type { StudentResolvers } from './../../../types.generated';
2-
export const Student: Pick<StudentResolvers, 'guardians'> = {
2+
export const Student: Pick<StudentResolvers, 'guardians' | '__isTypeOf'> = {
33
/* Implement Student resolver logic here */
44
};
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import type { StudentResolvers } from './../../../types.generated';
2-
export const Student: Pick<StudentResolvers, 'profile'> = {
2+
export const Student: Pick<StudentResolvers, 'profile' | '__isTypeOf'> = {
33
/* Implement Student resolver logic here */
44
};
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { StudentProfileResolvers } from './../../../types.generated';
22
export const StudentProfile: Pick<
33
StudentProfileResolvers,
4-
'email' | 'firstName' | 'id' | 'lastName' | 'phoneNumber'
4+
'email' | 'firstName' | 'id' | 'lastName' | 'phoneNumber' | '__isTypeOf'
55
> = {
66
/* Implement StudentProfile resolver logic here */
77
};
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import type { StudentResolvers } from './../../../types.generated';
2-
export const Student: Pick<StudentResolvers, 'id'> = {
2+
export const Student: Pick<StudentResolvers, 'id' | '__isTypeOf'> = {
33
/* Implement Student resolver logic here */
44
};
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import type { TeacherProfileResolvers } from './../../../types.generated';
2-
export const TeacherProfile: Pick<TeacherProfileResolvers, 'avatar'> = {
2+
export const TeacherProfile: Pick<
3+
TeacherProfileResolvers,
4+
'avatar' | '__isTypeOf'
5+
> = {
36
/* Implement TeacherProfile resolver logic here */
47
};
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import type { TeacherResolvers } from './../../../types.generated';
2-
export const Teacher: Pick<TeacherResolvers, 'profile'> = {
2+
export const Teacher: Pick<TeacherResolvers, 'profile' | '__isTypeOf'> = {
33
/* Implement Teacher resolver logic here */
44
};
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import type { TeacherProfileResolvers } from './../../../types.generated';
22
export const TeacherProfile: Pick<
33
TeacherProfileResolvers,
4-
'email' | 'firstName' | 'id' | 'lastName' | 'phoneNumber'
4+
'email' | 'firstName' | 'id' | 'lastName' | 'phoneNumber' | '__isTypeOf'
55
> = {
66
/* Implement TeacherProfile resolver logic here */
77
};
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import type { TeacherResolvers } from './../../../types.generated';
2-
export const Teacher: Pick<TeacherResolvers, 'id'> = {
2+
export const Teacher: Pick<TeacherResolvers, 'id' | '__isTypeOf'> = {
33
/* Implement Teacher resolver logic here */
44
};

packages/typescript-resolver-files-e2e/src/test-resolver-generation/schema-disabled/topic/resolvers/User.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import type { UserResolvers } from './../../types.generated';
88
*
99
* If you want to skip this file generation, remove the mapper or update the pattern in the `resolverGeneration.object` config.
1010
*/
11-
export const User: Pick<UserResolvers, 'topics'> = {
11+
export const User: Pick<UserResolvers, 'topics' | '__isTypeOf'> = {
1212
/* Implement User resolver logic here */
1313
topics: async (_parent, _arg, _ctx) => {
1414
/* User.topics resolver is required because User.topics exists but UserMapper.topics does not */

packages/typescript-resolver-files-e2e/src/test-resolver-generation/schema-disabled/user/resolvers/User.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export const User: Pick<
1818
| 'avatar'
1919
| 'id'
2020
| 'name'
21+
| '__isTypeOf'
2122
> = {
2223
/* Implement User resolver logic here */
2324
accountGitHub: async (_parent, _arg, _ctx) => {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import type { UserResolvers } from './../../types.generated';
2-
export const User: Pick<UserResolvers, 'pets'> = {
2+
export const User: Pick<UserResolvers, 'pets' | '__isTypeOf'> = {
33
/* Implement User resolver logic here */
44
};
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import type { ProfileResolvers } from './../../types.generated';
2-
export const Profile: Pick<ProfileResolvers, 'zoo'> = {
2+
export const Profile: Pick<ProfileResolvers, 'zoo' | '__isTypeOf'> = {
33
/* Implement Profile resolver logic here */
44
};

packages/typescript-resolver-files/src/generateResolverFiles/handleGraphQLObjectType.ts

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -64,35 +64,38 @@ export const handleGraphQLObjectType: GraphQLTypeHandler<
6464
return '';
6565
})();
6666

67-
if (fieldsToPick.length > 0 && pickReferenceResolver) {
67+
const hasFieldsToPick = fieldsToPick.length > 0;
68+
69+
// __isTypeOf is required to resolve abstract types, should user chooses this approach.
70+
// TODO: Run static analysis to enforce only one __isTypeOf per Object type, so users cannot accidentally implement/override it across modules
71+
if (hasFieldsToPick) {
72+
fieldsToPick.push('__isTypeOf');
73+
}
74+
75+
if (hasFieldsToPick && pickReferenceResolver) {
6876
fieldsToPick.push('__resolveReference');
6977
}
7078

7179
// `typeString` contains the resolver type
7280
// If there's fieldsToPick, we must only pick said fields from the original resolver type
73-
const typeString =
74-
fieldsToPick.length > 0
75-
? `Pick<${resolversTypeMeta.typeString}, ${fieldsToPick
76-
.map((fieldName) => `'${fieldName}'`)
77-
.join('|')}>`
78-
: resolversTypeMeta.typeString;
81+
const typeString = hasFieldsToPick
82+
? `Pick<${resolversTypeMeta.typeString}, ${fieldsToPick
83+
.map((fieldName) => `'${fieldName}'`)
84+
.join('|')}>`
85+
: resolversTypeMeta.typeString;
7986

8087
// Array of all resolvers that may need type checking
8188
// If there's fieldsToPick, we must only generate said fields
8289
const allResolversToGenerate =
8390
graphQLObjectTypeResolversToGenerate[resolverName];
84-
const resolversToGenerate =
85-
fieldsToPick.length > 0
86-
? fieldsToPick.reduce<typeof allResolversToGenerate>(
87-
(res, fieldToPick) => {
88-
if (allResolversToGenerate) {
89-
res[fieldToPick] = allResolversToGenerate[fieldToPick];
90-
}
91-
return res;
92-
},
93-
{}
94-
)
95-
: allResolversToGenerate;
91+
const resolversToGenerate = hasFieldsToPick
92+
? fieldsToPick.reduce<typeof allResolversToGenerate>((res, fieldToPick) => {
93+
if (allResolversToGenerate && allResolversToGenerate[fieldToPick]) {
94+
res[fieldToPick] = allResolversToGenerate[fieldToPick];
95+
}
96+
return res;
97+
}, {})
98+
: allResolversToGenerate;
9699

97100
const variableStatement = `${forcedGenerationWarning}export const ${resolverName}: ${typeString} = {
98101
/* Implement ${resolverName} resolver logic here */

0 commit comments

Comments
 (0)