Skip to content

Commit 32dfe76

Browse files
authored
feat: support casting enum values as types (#150)
Support casting enums as types when `useTypeImports` and `enumAsTypes` are true
1 parent 336e017 commit 32dfe76

File tree

5 files changed

+234
-2
lines changed

5 files changed

+234
-2
lines changed

src/index.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ type Options<T = TypeNode> = {
3434
generateLibrary: 'casual' | 'faker';
3535
fieldGeneration?: TypeFieldMap;
3636
enumsAsTypes?: boolean;
37+
useTypeImports?: boolean;
3738
useImplementingTypes: boolean;
3839
defaultNullableToNull: boolean;
3940
nonNull: boolean;
@@ -319,7 +320,9 @@ const getNamedType = (opts: Options<NamedTypeNode | ObjectTypeDefinitionNode>):
319320
const value = foundType.values ? foundType.values[0] : '';
320321
return handleValueGeneration(opts, undefined, () =>
321322
opts.enumsAsTypes
322-
? `'${value}'`
323+
? opts.useTypeImports
324+
? `('${value}' as ${typenameConverter(foundType.name, opts.enumsPrefix)})`
325+
: `'${value}'`
323326
: `${typenameConverter(foundType.name, opts.enumsPrefix)}.${enumConverter(value)}`,
324327
);
325328
}
@@ -487,7 +490,7 @@ const getImportTypes = ({
487490
? [enumsPrefix.slice(0, -1)]
488491
: types.filter(({ type }) => type === 'enum').map(({ name }) => typenameConverter(name, enumsPrefix));
489492

490-
if (!enumsAsTypes) {
493+
if (!enumsAsTypes || useTypeImports) {
491494
typeImports.push(...enumTypes);
492495
}
493496

@@ -587,6 +590,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
587590
const dynamicValues = !!config.dynamicValues;
588591
const generateLibrary = config.generateLibrary || 'casual';
589592
const enumsAsTypes = config.enumsAsTypes ?? false;
593+
const useTypeImports = config.useTypeImports ?? false;
590594
const useImplementingTypes = config.useImplementingTypes ?? false;
591595
const defaultNullableToNull = config.defaultNullableToNull ?? false;
592596

@@ -667,6 +671,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
667671
generateLibrary,
668672
fieldGeneration: config.fieldGeneration,
669673
enumsAsTypes,
674+
useTypeImports,
670675
useImplementingTypes,
671676
defaultNullableToNull,
672677
nonNull: false,
@@ -703,6 +708,7 @@ export const plugin: PluginFunction<TypescriptMocksPluginConfig> = (schema, docu
703708
generateLibrary,
704709
fieldGeneration: config.fieldGeneration,
705710
enumsAsTypes,
711+
useTypeImports,
706712
useImplementingTypes,
707713
defaultNullableToNull,
708714
nonNull: false,

tests/__snapshots__/typescript-mock-data.spec.ts.snap

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2227,6 +2227,85 @@ export const aQuery = (overrides?: Partial<Query>): Query => {
22272227
"
22282228
`;
22292229

2230+
exports[`should generate mock data with enum values as string union type if enumsAsTypes is true and cast the type if useTypeImports 1`] = `
2231+
"
2232+
export const anAvatar = (overrides?: Partial<Avatar>): Avatar => {
2233+
return {
2234+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '0550ff93-dd31-49b4-8c38-ff1cb68bdc38',
2235+
url: overrides && overrides.hasOwnProperty('url') ? overrides.url! : 'aliquid',
2236+
};
2237+
};
2238+
2239+
export const aUser = (overrides?: Partial<User>): User => {
2240+
return {
2241+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : 'a5756f00-41a6-422a-8a7d-d13ee6a63750',
2242+
creationDate: overrides && overrides.hasOwnProperty('creationDate') ? overrides.creationDate! : '1970-01-09T16:33:21.532Z',
2243+
login: overrides && overrides.hasOwnProperty('login') ? overrides.login! : 'libero',
2244+
avatar: overrides && overrides.hasOwnProperty('avatar') ? overrides.avatar! : anAvatar(),
2245+
status: overrides && overrides.hasOwnProperty('status') ? overrides.status! : ('ONLINE' as Status),
2246+
customStatus: overrides && overrides.hasOwnProperty('customStatus') ? overrides.customStatus! : ('hasXYZStatus' as AbcStatus),
2247+
scalarValue: overrides && overrides.hasOwnProperty('scalarValue') ? overrides.scalarValue! : 'neque',
2248+
camelCaseThing: overrides && overrides.hasOwnProperty('camelCaseThing') ? overrides.camelCaseThing! : aCamelCaseThing(),
2249+
unionThing: overrides && overrides.hasOwnProperty('unionThing') ? overrides.unionThing! : anAvatar(),
2250+
prefixedEnum: overrides && overrides.hasOwnProperty('prefixedEnum') ? overrides.prefixedEnum! : ('PREFIXED_VALUE' as PrefixedEnum),
2251+
};
2252+
};
2253+
2254+
export const aWithAvatar = (overrides?: Partial<WithAvatar>): WithAvatar => {
2255+
return {
2256+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '89f515e7-31e0-461d-a230-c4c7f4dafc5c',
2257+
avatar: overrides && overrides.hasOwnProperty('avatar') ? overrides.avatar! : anAvatar(),
2258+
};
2259+
};
2260+
2261+
export const aCamelCaseThing = (overrides?: Partial<CamelCaseThing>): CamelCaseThing => {
2262+
return {
2263+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '345b9cf9-00fa-4974-800f-aeee5ee7fd42',
2264+
};
2265+
};
2266+
2267+
export const aPrefixedResponse = (overrides?: Partial<PrefixedResponse>): PrefixedResponse => {
2268+
return {
2269+
ping: overrides && overrides.hasOwnProperty('ping') ? overrides.ping! : 'sunt',
2270+
};
2271+
};
2272+
2273+
export const anAbcType = (overrides?: Partial<AbcType>): AbcType => {
2274+
return {
2275+
abc: overrides && overrides.hasOwnProperty('abc') ? overrides.abc! : 'sit',
2276+
};
2277+
};
2278+
2279+
export const aListType = (overrides?: Partial<ListType>): ListType => {
2280+
return {
2281+
stringList: overrides && overrides.hasOwnProperty('stringList') ? overrides.stringList! : ['voluptatem'],
2282+
nullableStringList: overrides && overrides.hasOwnProperty('nullableStringList') ? overrides.nullableStringList! : ['eum'],
2283+
};
2284+
};
2285+
2286+
export const anUpdateUserInput = (overrides?: Partial<UpdateUserInput>): UpdateUserInput => {
2287+
return {
2288+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '1d6a9360-c92b-4660-8e5f-04155047bddc',
2289+
login: overrides && overrides.hasOwnProperty('login') ? overrides.login! : 'qui',
2290+
avatar: overrides && overrides.hasOwnProperty('avatar') ? overrides.avatar! : anAvatar(),
2291+
};
2292+
};
2293+
2294+
export const aMutation = (overrides?: Partial<Mutation>): Mutation => {
2295+
return {
2296+
updateUser: overrides && overrides.hasOwnProperty('updateUser') ? overrides.updateUser! : aUser(),
2297+
};
2298+
};
2299+
2300+
export const aQuery = (overrides?: Partial<Query>): Query => {
2301+
return {
2302+
user: overrides && overrides.hasOwnProperty('user') ? overrides.user! : aUser(),
2303+
prefixed_query: overrides && overrides.hasOwnProperty('prefixed_query') ? overrides.prefixed_query! : aPrefixedResponse(),
2304+
};
2305+
};
2306+
"
2307+
`;
2308+
22302309
exports[`should generate mock data with typename if addTypename is true 1`] = `
22312310
"
22322311
export const anAvatar = (overrides?: Partial<Avatar>): { __typename: 'Avatar' } & Avatar => {
@@ -3113,6 +3192,86 @@ export const aQuery = (overrides?: Partial<Query>): Query => {
31133192
"
31143193
`;
31153194

3195+
exports[`should preserve underscores if transformUnderscore is false and enumsAsTypes is true as cast the enum type if useTypeImports is true 1`] = `
3196+
"import type { Avatar, User, WithAvatar, CamelCaseThing, Prefixed_Response, AbcType, ListType, UpdateUserInput, Mutation, Query, AbcStatus, Status, Prefixed_Enum } from './types/graphql';
3197+
3198+
export const anAvatar = (overrides?: Partial<Avatar>): Avatar => {
3199+
return {
3200+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '0550ff93-dd31-49b4-8c38-ff1cb68bdc38',
3201+
url: overrides && overrides.hasOwnProperty('url') ? overrides.url! : 'aliquid',
3202+
};
3203+
};
3204+
3205+
export const aUser = (overrides?: Partial<User>): User => {
3206+
return {
3207+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : 'a5756f00-41a6-422a-8a7d-d13ee6a63750',
3208+
creationDate: overrides && overrides.hasOwnProperty('creationDate') ? overrides.creationDate! : '1970-01-09T16:33:21.532Z',
3209+
login: overrides && overrides.hasOwnProperty('login') ? overrides.login! : 'libero',
3210+
avatar: overrides && overrides.hasOwnProperty('avatar') ? overrides.avatar! : anAvatar(),
3211+
status: overrides && overrides.hasOwnProperty('status') ? overrides.status! : ('ONLINE' as Status),
3212+
customStatus: overrides && overrides.hasOwnProperty('customStatus') ? overrides.customStatus! : ('hasXYZStatus' as AbcStatus),
3213+
scalarValue: overrides && overrides.hasOwnProperty('scalarValue') ? overrides.scalarValue! : 'neque',
3214+
camelCaseThing: overrides && overrides.hasOwnProperty('camelCaseThing') ? overrides.camelCaseThing! : aCamelCaseThing(),
3215+
unionThing: overrides && overrides.hasOwnProperty('unionThing') ? overrides.unionThing! : anAvatar(),
3216+
prefixedEnum: overrides && overrides.hasOwnProperty('prefixedEnum') ? overrides.prefixedEnum! : ('PREFIXED_VALUE' as Prefixed_Enum),
3217+
};
3218+
};
3219+
3220+
export const aWithAvatar = (overrides?: Partial<WithAvatar>): WithAvatar => {
3221+
return {
3222+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '89f515e7-31e0-461d-a230-c4c7f4dafc5c',
3223+
avatar: overrides && overrides.hasOwnProperty('avatar') ? overrides.avatar! : anAvatar(),
3224+
};
3225+
};
3226+
3227+
export const aCamelCaseThing = (overrides?: Partial<CamelCaseThing>): CamelCaseThing => {
3228+
return {
3229+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '345b9cf9-00fa-4974-800f-aeee5ee7fd42',
3230+
};
3231+
};
3232+
3233+
export const aPrefixed_Response = (overrides?: Partial<Prefixed_Response>): Prefixed_Response => {
3234+
return {
3235+
ping: overrides && overrides.hasOwnProperty('ping') ? overrides.ping! : 'sunt',
3236+
};
3237+
};
3238+
3239+
export const anAbcType = (overrides?: Partial<AbcType>): AbcType => {
3240+
return {
3241+
abc: overrides && overrides.hasOwnProperty('abc') ? overrides.abc! : 'sit',
3242+
};
3243+
};
3244+
3245+
export const aListType = (overrides?: Partial<ListType>): ListType => {
3246+
return {
3247+
stringList: overrides && overrides.hasOwnProperty('stringList') ? overrides.stringList! : ['voluptatem'],
3248+
nullableStringList: overrides && overrides.hasOwnProperty('nullableStringList') ? overrides.nullableStringList! : ['eum'],
3249+
};
3250+
};
3251+
3252+
export const anUpdateUserInput = (overrides?: Partial<UpdateUserInput>): UpdateUserInput => {
3253+
return {
3254+
id: overrides && overrides.hasOwnProperty('id') ? overrides.id! : '1d6a9360-c92b-4660-8e5f-04155047bddc',
3255+
login: overrides && overrides.hasOwnProperty('login') ? overrides.login! : 'qui',
3256+
avatar: overrides && overrides.hasOwnProperty('avatar') ? overrides.avatar! : anAvatar(),
3257+
};
3258+
};
3259+
3260+
export const aMutation = (overrides?: Partial<Mutation>): Mutation => {
3261+
return {
3262+
updateUser: overrides && overrides.hasOwnProperty('updateUser') ? overrides.updateUser! : aUser(),
3263+
};
3264+
};
3265+
3266+
export const aQuery = (overrides?: Partial<Query>): Query => {
3267+
return {
3268+
user: overrides && overrides.hasOwnProperty('user') ? overrides.user! : aUser(),
3269+
prefixed_query: overrides && overrides.hasOwnProperty('prefixed_query') ? overrides.prefixed_query! : aPrefixed_Response(),
3270+
};
3271+
};
3272+
"
3273+
`;
3274+
31163275
exports[`should use relationshipsToOmit argument to terminate circular relationships with terminateCircularRelationships enabled 1`] = `
31173276
"
31183277
export const anAvatar = (overrides?: Partial<Avatar>, _relationshipsToOmit: Set<string> = new Set()): Avatar => {

tests/enumValues/__snapshots__/spec.ts.snap

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,21 @@ export const aMyType = (overrides?: Partial<MyType>): MyType => {
120120
"
121121
`;
122122
123+
exports[`enumValues config having 'keep' value should have no effect if 'transformUnderscore' is false and 'useTypeImports' is true 1`] = `
124+
"
125+
export const aMyType = (overrides?: Partial<MyType>): MyType => {
126+
return {
127+
underscoreEnum: overrides && overrides.hasOwnProperty('underscoreEnum') ? overrides.underscoreEnum! : ('_id' as UnderscoreEnum),
128+
pascalCaseEnum: overrides && overrides.hasOwnProperty('pascalCaseEnum') ? overrides.pascalCaseEnum! : ('PascalCase' as PascalCaseEnum),
129+
camelCaseEnum: overrides && overrides.hasOwnProperty('camelCaseEnum') ? overrides.camelCaseEnum! : ('camelCase' as CamelCaseEnum),
130+
snakeCaseEnum: overrides && overrides.hasOwnProperty('snakeCaseEnum') ? overrides.snakeCaseEnum! : ('snake_case' as SnakeCaseEnum),
131+
screamingSnakeCaseEnum: overrides && overrides.hasOwnProperty('screamingSnakeCaseEnum') ? overrides.screamingSnakeCaseEnum! : ('SCREAMING_SNAKE_CASE' as ScreamingSnakeCaseEnum),
132+
pascalCase_withUnderscore: overrides && overrides.hasOwnProperty('pascalCase_withUnderscore') ? overrides.pascalCase_withUnderscore! : ('other_snake_case' as PascalCase_WithUnderscore),
133+
};
134+
};
135+
"
136+
`;
137+
123138
exports[`enumValues config having 'keep' value should keep case 1`] = `
124139
"
125140
export const aMyType = (overrides?: Partial<MyType>): MyType => {

tests/enumValues/spec.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,24 @@ describe('enumValues config', () => {
5050
expect(result).toContain('other_snake_case');
5151
expect(result).toMatchSnapshot();
5252
});
53+
54+
it(`should have no effect if 'transformUnderscore' is false and 'useTypeImports' is true`, async () => {
55+
const result = await plugin(enumSchema, [], {
56+
enumValues: 'keep',
57+
transformUnderscore: false,
58+
enumsAsTypes: true,
59+
useTypeImports: true,
60+
});
61+
62+
expect(result).toBeDefined();
63+
expect(result).toContain('_id');
64+
expect(result).toContain(`('PascalCase' as PascalCaseEnum)`);
65+
expect(result).toContain(`('camelCase' as CamelCaseEnum)`);
66+
expect(result).toContain(`('snake_case' as SnakeCaseEnum)`);
67+
expect(result).toContain(`('SCREAMING_SNAKE_CASE' as ScreamingSnakeCaseEnum)`);
68+
expect(result).toContain(`('other_snake_case' as PascalCase_WithUnderscore)`);
69+
expect(result).toMatchSnapshot();
70+
});
5371
});
5472

5573
describe(`having default value`, () => {

tests/typescript-mock-data.spec.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,19 @@ it('should generate mock data with enum values as string union type if enumsAsTy
195195
expect(result).toMatchSnapshot();
196196
});
197197

198+
it('should generate mock data with enum values as string union type if enumsAsTypes is true and cast the type if useTypeImports', async () => {
199+
const result = await plugin(testSchema, [], { enumsAsTypes: true, useTypeImports: true });
200+
201+
expect(result).toBeDefined();
202+
expect(result).not.toContain('Status.Online');
203+
expect(result).toContain(`('ONLINE' as Status)`);
204+
expect(result).not.toContain('ABCStatus.hasXYZStatus');
205+
expect(result).toContain(`('hasXYZStatus' as AbcStatus)`);
206+
expect(result).not.toContain('Prefixed_Enum.PREFIXED_VALUE');
207+
expect(result).toContain(`('PREFIXED_VALUE' as PrefixedEnum)`);
208+
expect(result).toMatchSnapshot();
209+
});
210+
198211
it('should generate mock data with as-is enum values as string union type if enumsAsTypes is true and enumValues is "keep"', async () => {
199212
const result = await plugin(testSchema, [], { enumsAsTypes: true, enumValues: 'keep' });
200213

@@ -457,6 +470,27 @@ it('should preserve underscores if transformUnderscore is false and enumsAsTypes
457470
expect(result).toMatchSnapshot();
458471
});
459472

473+
it('should preserve underscores if transformUnderscore is false and enumsAsTypes is true as cast the enum type if useTypeImports is true', async () => {
474+
const result = await plugin(testSchema, [], {
475+
transformUnderscore: false,
476+
typesFile: './types/graphql.ts',
477+
enumsAsTypes: true,
478+
useTypeImports: true,
479+
});
480+
481+
expect(result).toBeDefined();
482+
expect(result).toContain(
483+
"import type { Avatar, User, WithAvatar, CamelCaseThing, Prefixed_Response, AbcType, ListType, UpdateUserInput, Mutation, Query, AbcStatus, Status, Prefixed_Enum } from './types/graphql';",
484+
);
485+
expect(result).toContain(
486+
'export const aPrefixed_Response = (overrides?: Partial<Prefixed_Response>): Prefixed_Response => {',
487+
);
488+
expect(result).toContain(
489+
"prefixedEnum: overrides && overrides.hasOwnProperty('prefixedEnum') ? overrides.prefixedEnum! : ('PREFIXED_VALUE' as Prefixed_Enum),",
490+
);
491+
expect(result).toMatchSnapshot();
492+
});
493+
460494
it('should generate single list element', async () => {
461495
const result = await plugin(testSchema, [], {
462496
typesFile: './types/graphql.ts',

0 commit comments

Comments
 (0)