Skip to content

Commit 9005cc1

Browse files
authoredJul 5, 2021
feat: allow returning string literal types for resolvers returning enum values (#6237)
1 parent 15b18aa commit 9005cc1

File tree

5 files changed

+72
-17
lines changed

5 files changed

+72
-17
lines changed
 

‎.changeset/swift-fishes-draw.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@graphql-codegen/visitor-plugin-common': minor
3+
'@graphql-codegen/typescript-resolvers': minor
4+
'@graphql-codegen/typescript': minor
5+
---
6+
7+
add `allowEnumStringTypes` option for allowing string literals as valid return types from resolvers in addition to enum values.\_

‎packages/plugins/other/visitor-plugin-common/src/base-visitor.ts

+6
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ export interface ParsedConfig {
3131
immutableTypes: boolean;
3232
useTypeImports: boolean;
3333
dedupeFragments: boolean;
34+
allowEnumStringTypes: boolean;
3435
}
3536

3637
export interface RawConfig {
@@ -195,6 +196,10 @@ export interface RawConfig {
195196
* @default false
196197
*/
197198
dedupeFragments?: boolean;
199+
/**
200+
* @ignore
201+
*/
202+
allowEnumStringTypes?: boolean;
198203
}
199204

200205
export class BaseVisitor<TRawConfig extends RawConfig = RawConfig, TPluginConfig extends ParsedConfig = ParsedConfig> {
@@ -213,6 +218,7 @@ export class BaseVisitor<TRawConfig extends RawConfig = RawConfig, TPluginConfig
213218
nonOptionalTypename: !!rawConfig.nonOptionalTypename,
214219
useTypeImports: !!rawConfig.useTypeImports,
215220
dedupeFragments: !!rawConfig.dedupeFragments,
221+
allowEnumStringTypes: !!rawConfig.allowEnumStringTypes,
216222
...((additionalConfig || {}) as any),
217223
};
218224

‎packages/plugins/typescript/typescript/src/config.ts

+10
Original file line numberDiff line numberDiff line change
@@ -276,4 +276,14 @@ export interface TypeScriptPluginConfig extends RawTypesConfig {
276276
* ```
277277
*/
278278
entireFieldWrapperValue?: string;
279+
/**
280+
* @description Allow using enum string values directly.
281+
*
282+
* @exampleMarkdown
283+
* ```yml
284+
* config:
285+
* allowEnumStringTypes: true
286+
* ```
287+
*/
288+
allowEnumStringTypes?: boolean;
279289
}

‎packages/plugins/typescript/typescript/src/visitor.ts

+26-17
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ export class TsVisitor<
9292
}
9393

9494
protected _getTypeForNode(node: NamedTypeNode): string {
95-
const typeAsString = (node.name as any) as string;
95+
const typeAsString = node.name as any as string;
9696

9797
if (this.config.useImplementingTypes) {
9898
const allTypesMap = this._schema.getTypeMap();
@@ -114,7 +114,16 @@ export class TsVisitor<
114114
}
115115
}
116116

117-
return super._getTypeForNode(node);
117+
const typeString = super._getTypeForNode(node);
118+
119+
if (this.config.allowEnumStringTypes === true) {
120+
const schemaType = this._schema.getType(node.name as any as string);
121+
if (isEnumType(schemaType)) {
122+
return `${typeString} | ` + '`${' + typeString + '}`';
123+
}
124+
}
125+
126+
return typeString;
118127
}
119128

120129
public getWrapperDefinitions(): string[] {
@@ -193,7 +202,7 @@ export class TsVisitor<
193202
.export()
194203
.asKind('type')
195204
.withName(this.convertName(node))
196-
.withComment((node.description as any) as string)
205+
.withComment(node.description as any as string)
197206
.withContent(possibleTypes).string;
198207
// return super.UnionTypeDefinition(node, key, parent).concat(withFutureAddedValue).join("");
199208
}
@@ -211,7 +220,7 @@ export class TsVisitor<
211220
FieldDefinition(node: FieldDefinitionNode, key?: number | string, parent?: any): string {
212221
const typeString = this.config.wrapEntireDefinitions
213222
? `EntireFieldWrapper<${node.type}>`
214-
: ((node.type as any) as string);
223+
: (node.type as any as string);
215224
const originalFieldNode = parent[key] as FieldDefinitionNode;
216225
const addOptionalSign = !this.config.avoidOptionals.field && originalFieldNode.type.kind !== Kind.NON_NULL_TYPE;
217226
const comment = this.getFieldComment(node);
@@ -233,7 +242,7 @@ export class TsVisitor<
233242
!this.config.avoidOptionals.inputValue &&
234243
(originalFieldNode.type.kind !== Kind.NON_NULL_TYPE ||
235244
(!this.config.avoidOptionals.defaultValue && node.defaultValue !== undefined));
236-
const comment = transformComment((node.description as any) as string, 1);
245+
const comment = transformComment(node.description as any as string, 1);
237246
const { type } = this.config.declarationKind;
238247
return (
239248
comment +
@@ -246,7 +255,7 @@ export class TsVisitor<
246255
}
247256

248257
EnumTypeDefinition(node: EnumTypeDefinitionNode): string {
249-
const enumName = (node.name as any) as string;
258+
const enumName = node.name as any as string;
250259

251260
// In case of mapped external enum string
252261
if (this.config.enumValues[enumName] && this.config.enumValues[enumName].sourceFile) {
@@ -274,15 +283,15 @@ export class TsVisitor<
274283
return new DeclarationBlock(this._declarationBlockConfig)
275284
.export()
276285
.asKind('type')
277-
.withComment((node.description as any) as string)
286+
.withComment(node.description as any as string)
278287
.withName(enumTypeName)
279288
.withContent(
280289
'\n' +
281290
node.values
282291
.map(enumOption => {
283-
const name = (enumOption.name as unknown) as string;
292+
const name = enumOption.name as unknown as string;
284293
const enumValue: string | number = getValueFromConfig(name) ?? name;
285-
const comment = transformComment((enumOption.description as any) as string, 1);
294+
const comment = transformComment(enumOption.description as any as string, 1);
286295

287296
return comment + indent('| ' + wrapWithSingleQuotes(enumValue));
288297
})
@@ -294,17 +303,17 @@ export class TsVisitor<
294303
if (this.config.numericEnums) {
295304
const block = new DeclarationBlock(this._declarationBlockConfig)
296305
.export()
297-
.withComment((node.description as any) as string)
306+
.withComment(node.description as any as string)
298307
.withName(enumTypeName)
299308
.asKind('enum')
300309
.withBlock(
301310
node.values
302311
.map((enumOption, i) => {
303-
const valueFromConfig = getValueFromConfig((enumOption.name as unknown) as string);
312+
const valueFromConfig = getValueFromConfig(enumOption.name as unknown as string);
304313
const enumValue: string | number = valueFromConfig ?? i;
305-
const comment = transformComment((enumOption.description as any) as string, 1);
314+
const comment = transformComment(enumOption.description as any as string, 1);
306315

307-
return comment + indent((enumOption.name as unknown) as string) + ` = ${enumValue}`;
316+
return comment + indent(enumOption.name as unknown as string) + ` = ${enumValue}`;
308317
})
309318
.concat(...withFutureAddedValue)
310319
.join(',\n')
@@ -324,13 +333,13 @@ export class TsVisitor<
324333
.export()
325334
.asKind('const')
326335
.withName(enumTypeName)
327-
.withComment((node.description as any) as string)
336+
.withComment(node.description as any as string)
328337
.withBlock(
329338
node.values
330339
.map(enumOption => {
331340
const optionName = this.convertName(enumOption, { useTypesPrefix: false, transformUnderscore: true });
332-
const comment = transformComment((enumOption.description as any) as string, 1);
333-
const name = (enumOption.name as unknown) as string;
341+
const comment = transformComment(enumOption.description as any as string, 1);
342+
const name = enumOption.name as unknown as string;
334343
const enumValue: string | number = getValueFromConfig(name) ?? name;
335344

336345
return comment + indent(`${optionName}: ${wrapWithSingleQuotes(enumValue)}`);
@@ -345,7 +354,7 @@ export class TsVisitor<
345354
.export()
346355
.asKind(this.config.constEnums ? 'const enum' : 'enum')
347356
.withName(enumTypeName)
348-
.withComment((node.description as any) as string)
357+
.withComment(node.description as any as string)
349358
.withBlock(this.buildEnumValuesBlock(enumName, node.values)).string;
350359
}
351360

‎packages/plugins/typescript/typescript/tests/typescript.spec.ts

+23
Original file line numberDiff line numberDiff line change
@@ -2908,6 +2908,29 @@ describe('TypeScript', () => {
29082908

29092909
validateTs(result);
29102910
});
2911+
2912+
it('allowEnumStringTypes', async () => {
2913+
const schema = buildSchema(/* GraphQL */ `
2914+
enum MyEnum {
2915+
A
2916+
B
2917+
C
2918+
}
2919+
type Query {
2920+
a: MyEnum
2921+
}
2922+
`);
2923+
const result = (await plugin(
2924+
schema,
2925+
[],
2926+
{ allowEnumStringTypes: true },
2927+
{ outputFile: '' }
2928+
)) as Types.ComplexPluginOutput;
2929+
2930+
validateTs(result);
2931+
2932+
expect(result.content).toBeSimilarStringTo('a?: Maybe<MyEnum | `${MyEnum}`>;');
2933+
});
29112934
});
29122935

29132936
it('should not have [object Object]', async () => {

0 commit comments

Comments
 (0)
Please sign in to comment.