Skip to content

Commit

Permalink
[typescript-resolvers] Add excludeTypes config to resolversNonOptiona…
Browse files Browse the repository at this point in the history
…lTypename (#9339)
  • Loading branch information
AaronMoat authored May 11, 2023
1 parent d431f42 commit 50471e6
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 2 deletions.
27 changes: 27 additions & 0 deletions .changeset/long-owls-matter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
'@graphql-codegen/visitor-plugin-common': minor
'@graphql-codegen/typescript-resolvers': minor
---

Add excludeTypes config to resolversNonOptionalTypename

This disables the adding of `__typename` in resolver types for any specified typename. This could be useful e.g. if you're wanting to enable this for all new types going forward but not do a big migration.

Usage example:

```typescript
const config: CodegenConfig = {
schema: 'src/schema/**/*.graphql',
generates: {
'src/schema/types.ts': {
plugins: ['typescript', 'typescript-resolvers'],
config: {
resolversNonOptionalTypename: {
unionMember: true,
excludeTypes: ['MyType'],
}
}
},
},
};
```
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,49 @@ export interface RawResolversConfig extends RawConfig {
/**
* @description Makes `__typename` of resolver mappings non-optional without affecting the base types.
* @default false
*
* @exampleMarkdown
* ## Enable for all
*
* ```ts filename="codegen.ts"
* import type { CodegenConfig } from '@graphql-codegen/cli';
*
* const config: CodegenConfig = {
* // ...
* generates: {
* 'path/to/file': {
* plugins: ['typescript', 'typescript-resolver'],
* config: {
* resolversNonOptionalTypename: true // or { unionMember: true, interfaceImplementingType: true }
* },
* },
* },
* };
* export default config;
* ```
*
* ## Enable except for some types
*
* ```ts filename="codegen.ts"
* import type { CodegenConfig } from '@graphql-codegen/cli';
*
* const config: CodegenConfig = {
* // ...
* generates: {
* 'path/to/file': {
* plugins: ['typescript', 'typescript-resolver'],
* config: {
* resolversNonOptionalTypename: {
* unionMember: true,
* interfaceImplementingType: true,
* excludeTypes: ['MyType'],
* }
* },
* },
* },
* };
* export default config;
* ```
*/
resolversNonOptionalTypename?: boolean | ResolversNonOptionalTypenameConfig;
/**
Expand Down Expand Up @@ -876,10 +919,11 @@ export class BaseResolversVisitor<
const schemaType = allSchemaTypes[typeName];

if (isUnionType(schemaType)) {
const { unionMember, excludeTypes } = this.config.resolversNonOptionalTypename;
res[typeName] = this.getAbstractMembersType({
typeName,
memberTypes: schemaType.getTypes(),
isTypenameNonOptional: this.config.resolversNonOptionalTypename.unionMember,
isTypenameNonOptional: unionMember && !excludeTypes?.includes(typeName),
});
}
return res;
Expand Down Expand Up @@ -913,10 +957,12 @@ export class BaseResolversVisitor<
}
}

const { interfaceImplementingType, excludeTypes } = this.config.resolversNonOptionalTypename;

res[typeName] = this.getAbstractMembersType({
typeName,
memberTypes: implementingTypes,
isTypenameNonOptional: this.config.resolversNonOptionalTypename.interfaceImplementingType,
isTypenameNonOptional: interfaceImplementingType && !excludeTypes?.includes(typeName),
});
}

Expand Down
1 change: 1 addition & 0 deletions packages/plugins/other/visitor-plugin-common/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,5 @@ export type FragmentDirectives = {
export interface ResolversNonOptionalTypenameConfig {
unionMember?: boolean;
interfaceImplementingType?: boolean;
excludeTypes?: string[];
}
31 changes: 31 additions & 0 deletions packages/plugins/typescript/resolvers/tests/ts-resolvers.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,37 @@ export type MyTypeResolvers<ContextType = any, ParentType extends ResolversParen

expect(result.content).not.toBeSimilarStringTo('export type ResolversInterfaceTypes');
});

it('resolversNonOptionalTypename - excludes types', async () => {
const result = await plugin(
resolversTestingSchema,
[],
{
resolversNonOptionalTypename: {
unionMember: true,
interfaceImplementingType: true,
excludeTypes: ['ChildUnion', 'AnotherNode', 'Node'],
},
},
{ outputFile: '' }
);

expect(result.content).toBeSimilarStringTo(`
export type ResolversUnionTypes<RefType extends Record<string, unknown>> = {
ChildUnion: ( Child ) | ( MyOtherType );
MyUnion: ( Omit<MyType, 'unionChild'> & { unionChild?: Maybe<RefType['ChildUnion']> } & { __typename: 'MyType' } ) | ( MyOtherType & { __typename: 'MyOtherType' } );
};
`);

expect(result.content).toBeSimilarStringTo(`
export type ResolversInterfaceTypes<RefType extends Record<string, unknown>> = {
Node: ( SomeNode );
AnotherNode: ( Omit<AnotherNodeWithChild, 'unionChild'> & { unionChild?: Maybe<RefType['ChildUnion']> } ) | ( Omit<AnotherNodeWithAll, 'unionChild' | 'unionChildren'> & { unionChild?: Maybe<RefType['ChildUnion']>, unionChildren: Array<RefType['ChildUnion']> } );
WithChild: ( Omit<AnotherNodeWithChild, 'unionChild'> & { unionChild?: Maybe<RefType['ChildUnion']> } & { __typename: 'AnotherNodeWithChild' } ) | ( Omit<AnotherNodeWithAll, 'unionChild' | 'unionChildren'> & { unionChild?: Maybe<RefType['ChildUnion']>, unionChildren: Array<RefType['ChildUnion']> } & { __typename: 'AnotherNodeWithAll' } );
WithChildren: ( Omit<AnotherNodeWithAll, 'unionChild' | 'unionChildren'> & { unionChild?: Maybe<RefType['ChildUnion']>, unionChildren: Array<RefType['ChildUnion']> } & { __typename: 'AnotherNodeWithAll' } );
};
`);
});
});

it('directiveResolverMappings - should generate correct types (import definition)', async () => {
Expand Down

0 comments on commit 50471e6

Please sign in to comment.