Repository: apollographql/apollo-codegen (ts)

Issue:
Flow Modern flattens Arrays in output
The code generation for a standard Relay connection is generated like the following. Notice that the `edges` field is not generated as an array, but instead a single object type:
> ```ts
>   edges: ?GetFoosById_foos_edges,      // A list of edges.
> ```

Schema:
```graphql
type Foo {
  id: ID!
  description: String
}

# A connection to a list of items.
type FooConnection {
  # Information to aid in pagination.
  pageInfo: PageInfo!

  # A list of edges.
  edges: [FooEdge]
}

# An edge in a connection.
type FooEdge {
  # The item at the end of the edge
  node: Foo

  # A cursor for use in pagination
  cursor: String!
}
```

Generated:
```ts
export type GetFoosById_foos_pageInfo = {|
  __typename: "PageInfo",
  hasNextPage: boolean,     // When paginating forwards, are there more items?
  hasPreviousPage: boolean, // When paginating backwards, are there more items?
  startCursor: ?string,     // When paginating backwards, the cursor to continue.
  endCursor: ?string,       // When paginating forwards, the cursor to continue.
|};

export type GetFoosById_foos_edges_node = {|
  __typename: "Foo",
  id: string, // The ID of an object
  description: ?string,
|};

export type GetFoosById_foos_edges = {|
  __typename: "FooEdge",
  cursor: string,                     // A cursor for use in pagination
  node: ?GetFoosById_foos_edges_node, // The item at the end of the edge
|};

export type GetFoosById_foos = {|
  __typename: "FooConnection",
  pageInfo: GetFoosById_foos_pageInfo, // Information to aid in pagination.
  edges: ?GetFoosById_foos_edges,      // A list of edges.
|};

export type GetFoosById = {|
  foos: ?GetFoosById_foos
|};
```

Pull request:
Fix Flow-Modern incorrectly-flattened array generation
Fixes #325 (_Flow Modern flattens Arrays in output_)

This is an alternative implementation versus #342 which:
  * Provides full support of arbitrary nullable/nested lists without special casing top-level lists/nulls. (The `typeAnnotationFromGraphQLType` function now recursively covers all cases; the type annotations for field properties are now correctly generated from this same logic as well).
  * Does not include the inferred inflection change discussed in that PR, which is unrelated to the array-flattening issue

Patch:
diff --git a/src/flow/codeGeneration.js b/src/flow/codeGeneration.js
index 2c11f6fd..c5f0d8b2 100644
--- a/src/flow/codeGeneration.js
+++ b/src/flow/codeGeneration.js
@@ -23,6 +23,10 @@ import {
   wrap,
 } from '../utilities/printing';
 
+import {
+  sortEnumValues
+} from '../utilities/graphql';
+
 import CodeGenerator from '../utilities/CodeGenerator';
 
 import {
@@ -76,7 +80,7 @@ function enumerationDeclaration(generator, type) {
   }
   generator.printOnNewline(`export type ${name} =`);
   const nValues = values.length;
-  values.forEach((value, i) => {
+  sortEnumValues(values).forEach((value, i) => {
     if (!value.description || value.description.indexOf('\n') === -1) {
       generator.printOnNewline(`  "${value.value}"${i === nValues - 1 ? ';' : ' |'}${wrap(' // ', value.description)}`)
     } else {
diff --git a/src/javascript/flow/codeGeneration.ts b/src/javascript/flow/codeGeneration.ts
index 7af01be5..fa0a1dc4 100644
--- a/src/javascript/flow/codeGeneration.ts
+++ b/src/javascript/flow/codeGeneration.ts
@@ -3,7 +3,6 @@ import { stripIndent } from 'common-tags';
 import {
   GraphQLEnumType,
   GraphQLInputObjectType,
-  GraphQLNonNull,
 } from 'graphql';
 import * as path from 'path';
 
@@ -154,6 +153,7 @@ export class FlowAPIGenerator extends FlowGenerator {
     const {
       operationType,
       operationName,
+      variables,
       selectionSet
     } = operation;
 
@@ -178,6 +178,19 @@ export class FlowAPIGenerator extends FlowGenerator {
 
     this.printer.enqueue(exportedTypeAlias);
     this.scopeStackPop();
+
+    // Generate the variables interface if the operation has any variables
+    if (variables.length > 0) {
+      const interfaceName = operationName + 'Variables';
+      this.scopeStackPush(interfaceName);
+      this.printer.enqueue(this.exportDeclaration(
+        this.typeAliasObject(interfaceName, variables.map((variable) => ({
+          name: variable.name,
+          annotation: this.typeAnnotationFromGraphQLType(variable.type)
+        })), { keyInheritsNullability: true })
+      ));
+      this.scopeStackPop();
+    }
   }
 
   public typeAliasesForFragment(fragment: Fragment) {
@@ -288,9 +301,7 @@ export class FlowAPIGenerator extends FlowGenerator {
   ) {
     const { selectionSet } = field;
 
-    const selectionValueGeneratedTypeName = field.type instanceof GraphQLNonNull
-      ? generatedTypeName.id.name
-      : '?' + generatedTypeName.id.name;
+    const annotation = this.typeAnnotationFromGraphQLType(field.type, generatedTypeName.id.name);
 
     const typeCase = this.getTypeCasesForSelectionSet(selectionSet as SelectionSet);
     const variants = typeCase.exhaustiveVariants;
@@ -326,9 +337,7 @@ export class FlowAPIGenerator extends FlowGenerator {
     return {
       name: field.alias ? field.alias : field.name,
       description: field.description,
-      annotation: t.genericTypeAnnotation(
-        t.identifier(selectionValueGeneratedTypeName)
-      )
+      annotation: annotation,
     };
   }
 
diff --git a/src/javascript/flow/helpers.ts b/src/javascript/flow/helpers.ts
index 52f027b0..3cd44817 100644
--- a/src/javascript/flow/helpers.ts
+++ b/src/javascript/flow/helpers.ts
@@ -25,54 +25,35 @@ const builtInScalarMap = {
 export function createTypeAnnotationFromGraphQLTypeFunction(
   compilerOptions: CompilerOptions
 ): Function {
-  return function typeAnnotationFromGraphQLType(type: GraphQLType, {
-    nullable
-  } = {
-    nullable: true
-  }): t.FlowTypeAnnotation {
-    if (type instanceof GraphQLNonNull) {
-      return typeAnnotationFromGraphQLType(
-        type.ofType,
-        { nullable: false }
-      );
-    }
-
+  function nonNullableTypeAnnotationFromGraphQLType(type: GraphQLType, typeName?: string): t.FlowTypeAnnotation {
     if (type instanceof GraphQLList) {
-      const typeAnnotation = t.arrayTypeAnnotation(
-        typeAnnotationFromGraphQLType(type.ofType)
+      return t.arrayTypeAnnotation(
+        typeAnnotationFromGraphQLType(type.ofType, typeName)
       );
-
-      if (nullable) {
-        return t.nullableTypeAnnotation(typeAnnotation);
+    } else if (type instanceof GraphQLScalarType) {
+      const builtIn = builtInScalarMap[typeName || type.name]
+      if (builtIn != null) {
+        return builtIn;
+      } else if (compilerOptions.passthroughCustomScalars) {
+        return t.anyTypeAnnotation();
       } else {
-        return typeAnnotation;
-      }
-    }
-
-    let typeAnnotation;
-    if (type instanceof GraphQLScalarType) {
-      const builtIn = builtInScalarMap[type.name]
-      if (builtIn) {
-        typeAnnotation = builtIn;
-      } else {
-        if (compilerOptions.passthroughCustomScalars) {
-          typeAnnotation = t.anyTypeAnnotation();
-        } else {
-          typeAnnotation = t.genericTypeAnnotation(
-            t.identifier(type.name)
-          );
-        }
+        return t.genericTypeAnnotation(t.identifier(typeName || type.name));
       }
+    } else if (type instanceof GraphQLNonNull) {
+      // This won't happen; but for TypeScript completeness:
+      return typeAnnotationFromGraphQLType(type.ofType, typeName);
     } else {
-      typeAnnotation = t.genericTypeAnnotation(
-        t.identifier(type.name)
-      );
+      return t.genericTypeAnnotation(t.identifier(typeName || type.name));
     }
+  }
 
-    if (nullable) {
-      return t.nullableTypeAnnotation(typeAnnotation);
+  function typeAnnotationFromGraphQLType(type: GraphQLType, typeName?: string): t.FlowTypeAnnotation {
+    if (type instanceof GraphQLNonNull) {
+      return nonNullableTypeAnnotationFromGraphQLType(type.ofType, typeName);
     } else {
-      return typeAnnotation;
+      return t.nullableTypeAnnotation(nonNullableTypeAnnotationFromGraphQLType(type, typeName));
     }
   }
+
+  return typeAnnotationFromGraphQLType;
 }
diff --git a/src/javascript/flow/language.ts b/src/javascript/flow/language.ts
index cc8868b9..e86db04c 100644
--- a/src/javascript/flow/language.ts
+++ b/src/javascript/flow/language.ts
@@ -7,6 +7,10 @@ import {
   CompilerOptions
 } from '../../compiler';
 
+import {
+  sortEnumValues
+} from '../../utilities/graphql';
+
 import { createTypeAnnotationFromGraphQLTypeFunction } from './helpers';
 
 import * as t from '@babel/types';
@@ -33,7 +37,7 @@ export default class FlowGenerator {
 
   public enumerationDeclaration(type: GraphQLEnumType) {
     const { name, description } = type;
-    const unionValues = type.getValues().map(({ value }) => {
+    const unionValues = sortEnumValues(type.getValues()).map(({ value }) => {
       const type = t.stringLiteralTypeAnnotation();
       type.value = value;
 
@@ -84,18 +88,15 @@ export default class FlowGenerator {
   } = {}) {
     const objectTypeAnnotation = t.objectTypeAnnotation(
       fields.map(({name, description, annotation}) => {
-
         const objectTypeProperty = t.objectTypeProperty(
-          t.identifier(
-            // Nullable fields on input objects do not have to be defined
-            // as well, so allow these fields to be "undefined"
-            (keyInheritsNullability && annotation.type === "NullableTypeAnnotation")
-              ? name + '?'
-              : name
-          ),
+          t.identifier(name),
           annotation
         );
 
+        // Nullable fields on input objects do not have to be defined
+        // as well, so allow these fields to be "undefined"
+        objectTypeProperty.optional = keyInheritsNullability && annotation.type === "NullableTypeAnnotation";
+
         if (description) {
           objectTypeProperty.trailingComments = [{
             type: 'CommentLine',
diff --git a/src/javascript/types/augment-babel-types.ts b/src/javascript/types/augment-babel-types.ts
index f6cc11c0..54265763 100644
--- a/src/javascript/types/augment-babel-types.ts
+++ b/src/javascript/types/augment-babel-types.ts
@@ -9,6 +9,10 @@ declare module 'babel-types' {
   }
 
   interface ObjectTypeAnnotation {
-    exact: boolean
+    exact: boolean;
+  }
+
+  interface ObjectTypeProperty {
+    optional: boolean;
   }
 }
diff --git a/src/javascript/typescript/codeGeneration.ts b/src/javascript/typescript/codeGeneration.ts
index 79c1dec3..6423a087 100644
--- a/src/javascript/typescript/codeGeneration.ts
+++ b/src/javascript/typescript/codeGeneration.ts
@@ -144,6 +144,7 @@ export class TypescriptAPIGenerator extends TypescriptGenerator {
     const {
       operationType,
       operationName,
+      variables,
       selectionSet
     } = operation;
 
@@ -168,6 +169,19 @@ export class TypescriptAPIGenerator extends TypescriptGenerator {
 
     this.printer.enqueue(exportedTypeAlias);
     this.scopeStackPop();
+
+    // Generate the variables interface if the operation has any variables
+    if (variables.length > 0) {
+      const interfaceName = operationName + 'Variables';
+      this.scopeStackPush(interfaceName);
+      this.printer.enqueue(this.exportDeclaration(
+        this.interface(interfaceName, variables.map((variable) => ({
+          name: variable.name,
+          type: this.typeFromGraphQLType(variable.type)
+        })), { keyInheritsNullability: true })
+      ));
+      this.scopeStackPop();
+    }
   }
 
   public interfacesForFragment(fragment: Fragment) {
diff --git a/src/javascript/typescript/language.ts b/src/javascript/typescript/language.ts
index 3eeaa8f7..cc8b4535 100644
--- a/src/javascript/typescript/language.ts
+++ b/src/javascript/typescript/language.ts
@@ -8,6 +8,11 @@ import {
   CompilerOptions
 } from '../../compiler';
 
+
+import {
+  sortEnumValues
+} from '../../utilities/graphql';
+
 import { createTypeFromGraphQLTypeFunction, TypeFromGraphQLTypeOptions } from './helpers';
 
 import * as t from '@babel/types';
@@ -34,7 +39,7 @@ export default class TypescriptGenerator {
 
   public enumerationDeclaration(type: GraphQLEnumType) {
     const { name, description } = type;
-    const enumMembers = type.getValues().map(({ value }) => {
+    const enumMembers = sortEnumValues(type.getValues()).map(({ value }) => {
       return t.TSEnumMember(
         t.identifier(value),
         t.stringLiteral(value)
diff --git a/src/typescript/codeGeneration.ts b/src/typescript/codeGeneration.ts
index 46161fd7..bc8f10c0 100644
--- a/src/typescript/codeGeneration.ts
+++ b/src/typescript/codeGeneration.ts
@@ -18,6 +18,10 @@ import {
   wrap
 } from '../utilities/printing';
 
+import {
+  sortEnumValues
+} from '../utilities/graphql';
+
 import CodeGenerator from '../utilities/CodeGenerator';
 
 import {
@@ -73,13 +77,13 @@ function enumerationDeclaration(generator: CodeGenerator, type: GraphQLEnumType)
       })
   }
   generator.printOnNewline(`export enum ${name} {`);
-  values.sort((a, b) => a.value < b.value ? -1 : a.value > b.value ? 1 : 0).forEach((value) => {
+  sortEnumValues(values).forEach((value) => {
     if (!value.description || value.description.indexOf('\n') === -1) {
       generator.printOnNewline(`  ${value.value} = "${value.value}",${wrap(' // ', value.description)}`)
     } else {
       if (value.description) {
         value.description.split('\n')
-          .forEach(line => {
+          .forEach((line: string) => {
             generator.printOnNewline(`  // ${line.trim()}`);
           })
       }
diff --git a/src/utilities/graphql.ts b/src/utilities/graphql.ts
index 322bd359..738379fc 100644
--- a/src/utilities/graphql.ts
+++ b/src/utilities/graphql.ts
@@ -11,6 +11,7 @@ import {
   GraphQLObjectType,
   GraphQLInterfaceType,
   GraphQLUnionType,
+  GraphQLEnumValue,
   GraphQLError,
   GraphQLSchema,
   GraphQLType,
@@ -43,6 +44,10 @@ declare module "graphql/utilities/buildASTSchema" {
   ): GraphQLSchema;
 }
 
+export function sortEnumValues(values: GraphQLEnumValue[]): GraphQLEnumValue[] {
+  return values.sort((a, b) => a.value < b.value ? -1 : a.value > b.value ? 1 : 0);
+}
+
 export function isList(type: GraphQLType): boolean {
   return type instanceof GraphQLList || (type instanceof GraphQLNonNull && type.ofType instanceof GraphQLList);
 }


Test patch:
diff --git a/src/javascript/flow/__tests__/__snapshots__/codeGeneration.ts.snap b/src/javascript/flow/__tests__/__snapshots__/codeGeneration.ts.snap
index 894753a2..6bdcae98 100644
--- a/src/javascript/flow/__tests__/__snapshots__/codeGeneration.ts.snap
+++ b/src/javascript/flow/__tests__/__snapshots__/codeGeneration.ts.snap
@@ -22,21 +22,25 @@ export type HeroName_hero_Human_friends = {
 
 export type HeroName_hero = {
   __typename: \\"Human\\",
-  name: string,                          // What this human calls themselves
-  id: string,                            // The ID of the human
-  homePlanet: ?string,                   // The home planet of the human, or null if unknown
-  friends: ?HeroName_hero_Human_friends, // This human's friends, or an empty list if they have none
+  name: string,                               // What this human calls themselves
+  id: string,                                 // The ID of the human
+  homePlanet: ?string,                        // The home planet of the human, or null if unknown
+  friends: ?(?HeroName_hero_Human_friends)[], // This human's friends, or an empty list if they have none
 } | {
   __typename: \\"Droid\\",
-  name: string,                          // What others call this droid
-  id: string,                            // The ID of the droid
-  appearsIn: (?Episode)[],               // The movies this droid appears in
+  name: string,                               // What others call this droid
+  id: string,                                 // The ID of the droid
+  appearsIn: (?Episode)[],                    // The movies this droid appears in
 };
 
 export type HeroName = {
   hero: ?HeroName_hero
 };
 
+export type HeroNameVariables = {
+  episode?: ?Episode
+};
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -45,7 +49,7 @@ export type HeroName = {
 //==============================================================
 
 // The episodes in the Star Wars trilogy
-export type Episode = \\"NEWHOPE\\" | \\"EMPIRE\\" | \\"JEDI\\";
+export type Episode = \\"EMPIRE\\" | \\"JEDI\\" | \\"NEWHOPE\\";
 
 //==============================================================
 // END Enums and Input Objects
@@ -74,7 +78,7 @@ export type droidFragment = {
 //==============================================================
 
 // The episodes in the Star Wars trilogy
-export type Episode = \\"NEWHOPE\\" | \\"EMPIRE\\" | \\"JEDI\\";
+export type Episode = \\"EMPIRE\\" | \\"JEDI\\" | \\"NEWHOPE\\";
 
 //==============================================================
 // END Enums and Input Objects
@@ -100,8 +104,8 @@ export type humanFragment_friends = {
 
 export type humanFragment = {
   __typename: \\"Human\\",
-  homePlanet: ?string,             // The home planet of the human, or null if unknown
-  friends: ?humanFragment_friends, // This human's friends, or an empty list if they have none
+  homePlanet: ?string,                  // The home planet of the human, or null if unknown
+  friends: ?(?humanFragment_friends)[], // This human's friends, or an empty list if they have none
 };
 
 //==============================================================
@@ -112,7 +116,7 @@ export type humanFragment = {
 //==============================================================
 
 // The episodes in the Star Wars trilogy
-export type Episode = \\"NEWHOPE\\" | \\"EMPIRE\\" | \\"JEDI\\";
+export type Episode = \\"EMPIRE\\" | \\"JEDI\\" | \\"NEWHOPE\\";
 
 //==============================================================
 // END Enums and Input Objects
@@ -214,7 +218,7 @@ export type anotherFragment = anotherFragment_Droid | anotherFragment_Human;
 //==============================================================
 
 // The episodes in the Star Wars trilogy
-export type Episode = \\"NEWHOPE\\" | \\"EMPIRE\\" | \\"JEDI\\";
+export type Episode = \\"EMPIRE\\" | \\"JEDI\\" | \\"NEWHOPE\\";
 
 //==============================================================
 // END Enums and Input Objects
@@ -243,7 +247,7 @@ export type simpleFragment = {
 //==============================================================
 
 // The episodes in the Star Wars trilogy
-export type Episode = \\"NEWHOPE\\" | \\"EMPIRE\\" | \\"JEDI\\";
+export type Episode = \\"EMPIRE\\" | \\"JEDI\\" | \\"NEWHOPE\\";
 
 //==============================================================
 // END Enums and Input Objects
@@ -309,6 +313,10 @@ export type HeroInlineFragment = {
   hero: ?HeroInlineFragment_hero
 };
 
+export type HeroInlineFragmentVariables = {
+  episode?: ?Episode
+};
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -317,7 +325,7 @@ export type HeroInlineFragment = {
 //==============================================================
 
 // The episodes in the Star Wars trilogy
-export type Episode = \\"NEWHOPE\\" | \\"EMPIRE\\" | \\"JEDI\\";
+export type Episode = \\"EMPIRE\\" | \\"JEDI\\" | \\"NEWHOPE\\";
 
 //==============================================================
 // END Enums and Input Objects
@@ -345,21 +353,25 @@ export type HeroName_hero_Human_friends = {
 
 export type HeroName_hero = {
   __typename: \\"Human\\",
-  name: string,                          // What this human calls themselves
-  id: string,                            // The ID of the human
-  homePlanet: ?string,                   // The home planet of the human, or null if unknown
-  friends: ?HeroName_hero_Human_friends, // This human's friends, or an empty list if they have none
+  name: string,                               // What this human calls themselves
+  id: string,                                 // The ID of the human
+  homePlanet: ?string,                        // The home planet of the human, or null if unknown
+  friends: ?(?HeroName_hero_Human_friends)[], // This human's friends, or an empty list if they have none
 } | {
   __typename: \\"Droid\\",
-  name: string,                          // What others call this droid
-  id: string,                            // The ID of the droid
-  appearsIn: (?Episode)[],               // The movies this droid appears in
+  name: string,                               // What others call this droid
+  id: string,                                 // The ID of the droid
+  appearsIn: (?Episode)[],                    // The movies this droid appears in
 };
 
 export type HeroName = {
   hero: ?HeroName_hero
 };
 
+export type HeroNameVariables = {
+  episode?: ?Episode
+};
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -368,7 +380,7 @@ export type HeroName = {
 //==============================================================
 
 // The episodes in the Star Wars trilogy
-export type Episode = \\"NEWHOPE\\" | \\"EMPIRE\\" | \\"JEDI\\";
+export type Episode = \\"EMPIRE\\" | \\"JEDI\\" | \\"NEWHOPE\\";
 
 //==============================================================
 // END Enums and Input Objects
@@ -401,22 +413,26 @@ export type HeroName_hero_Droid_friends = {
 
 export type HeroName_hero = {
   __typename: \\"Human\\",
-  name: string,                          // What this human calls themselves
-  id: string,                            // The ID of the human
-  homePlanet: ?string,                   // The home planet of the human, or null if unknown
-  friends: ?HeroName_hero_Human_friends, // This human's friends, or an empty list if they have none
+  name: string,                               // What this human calls themselves
+  id: string,                                 // The ID of the human
+  homePlanet: ?string,                        // The home planet of the human, or null if unknown
+  friends: ?(?HeroName_hero_Human_friends)[], // This human's friends, or an empty list if they have none
 } | {
   __typename: \\"Droid\\",
-  name: string,                          // What others call this droid
-  id: string,                            // The ID of the droid
-  appearsIn: (?Episode)[],               // The movies this droid appears in
-  friends: ?HeroName_hero_Droid_friends, // This droid's friends, or an empty list if they have none
+  name: string,                               // What others call this droid
+  id: string,                                 // The ID of the droid
+  appearsIn: (?Episode)[],                    // The movies this droid appears in
+  friends: ?(?HeroName_hero_Droid_friends)[], // This droid's friends, or an empty list if they have none
 };
 
 export type HeroName = {
   hero: ?HeroName_hero
 };
 
+export type HeroNameVariables = {
+  episode?: ?Episode
+};
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -425,7 +441,7 @@ export type HeroName = {
 //==============================================================
 
 // The episodes in the Star Wars trilogy
-export type Episode = \\"NEWHOPE\\" | \\"EMPIRE\\" | \\"JEDI\\";
+export type Episode = \\"EMPIRE\\" | \\"JEDI\\" | \\"NEWHOPE\\";
 
 //==============================================================
 // END Enums and Input Objects
@@ -457,6 +473,10 @@ export type HeroName = {
   hero: ?HeroName_hero
 };
 
+export type HeroNameVariables = {
+  episode?: ?Episode
+};
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -465,7 +485,7 @@ export type HeroName = {
 //==============================================================
 
 // The episodes in the Star Wars trilogy
-export type Episode = \\"NEWHOPE\\" | \\"EMPIRE\\" | \\"JEDI\\";
+export type Episode = \\"EMPIRE\\" | \\"JEDI\\" | \\"NEWHOPE\\";
 
 // The input object sent when someone is creating a new review
 export type ReviewInput = {
@@ -510,6 +530,10 @@ export type SomeOther = {
   hero: ?SomeOther_hero
 };
 
+export type SomeOtherVariables = {
+  episode?: ?Episode
+};
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -518,7 +542,7 @@ export type SomeOther = {
 //==============================================================
 
 // The episodes in the Star Wars trilogy
-export type Episode = \\"NEWHOPE\\" | \\"EMPIRE\\" | \\"JEDI\\";
+export type Episode = \\"EMPIRE\\" | \\"JEDI\\" | \\"NEWHOPE\\";
 
 // The input object sent when someone is creating a new review
 export type ReviewInput = {
@@ -563,6 +587,11 @@ export type ReviewMovie = {
   createReview: ?ReviewMovie_createReview
 };
 
+export type ReviewMovieVariables = {
+  episode?: ?Episode,
+  review?: ?ReviewInput,
+};
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -571,7 +600,7 @@ export type ReviewMovie = {
 //==============================================================
 
 // The episodes in the Star Wars trilogy
-export type Episode = \\"NEWHOPE\\" | \\"EMPIRE\\" | \\"JEDI\\";
+export type Episode = \\"EMPIRE\\" | \\"JEDI\\" | \\"NEWHOPE\\";
 
 // The input object sent when someone is creating a new review
 export type ReviewInput = {
@@ -619,7 +648,7 @@ export type someFragment = {
 //==============================================================
 
 // The episodes in the Star Wars trilogy
-export type Episode = \\"NEWHOPE\\" | \\"EMPIRE\\" | \\"JEDI\\";
+export type Episode = \\"EMPIRE\\" | \\"JEDI\\" | \\"NEWHOPE\\";
 
 // The input object sent when someone is creating a new review
 export type ReviewInput = {
@@ -663,6 +692,10 @@ export type HeroFragment = {
   hero: ?HeroFragment_hero
 };
 
+export type HeroFragmentVariables = {
+  episode?: ?Episode
+};
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -671,7 +704,7 @@ export type HeroFragment = {
 //==============================================================
 
 // The episodes in the Star Wars trilogy
-export type Episode = \\"NEWHOPE\\" | \\"EMPIRE\\" | \\"JEDI\\";
+export type Episode = \\"EMPIRE\\" | \\"JEDI\\" | \\"NEWHOPE\\";
 
 //==============================================================
 // END Enums and Input Objects
@@ -700,7 +733,7 @@ export type simpleFragment = {
 //==============================================================
 
 // The episodes in the Star Wars trilogy
-export type Episode = \\"NEWHOPE\\" | \\"EMPIRE\\" | \\"JEDI\\";
+export type Episode = \\"EMPIRE\\" | \\"JEDI\\" | \\"NEWHOPE\\";
 
 //==============================================================
 // END Enums and Input Objects
@@ -762,6 +795,10 @@ export type HeroName = {
   hero: ?HeroName_hero
 };
 
+export type HeroNameVariables = {
+  episode?: ?Episode
+};
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -770,7 +807,7 @@ export type HeroName = {
 //==============================================================
 
 // The episodes in the Star Wars trilogy
-export type Episode = \\"NEWHOPE\\" | \\"EMPIRE\\" | \\"JEDI\\";
+export type Episode = \\"EMPIRE\\" | \\"JEDI\\" | \\"NEWHOPE\\";
 
 //==============================================================
 // END Enums and Input Objects
@@ -801,6 +838,11 @@ export type ReviewMovie = {
   createReview: ?ReviewMovie_createReview
 };
 
+export type ReviewMovieVariables = {
+  episode?: ?Episode,
+  review?: ?ReviewInput,
+};
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -809,7 +851,7 @@ export type ReviewMovie = {
 //==============================================================
 
 // The episodes in the Star Wars trilogy
-export type Episode = \\"NEWHOPE\\" | \\"EMPIRE\\" | \\"JEDI\\";
+export type Episode = \\"EMPIRE\\" | \\"JEDI\\" | \\"NEWHOPE\\";
 
 // The input object sent when someone is creating a new review
 export type ReviewInput = {
diff --git a/src/javascript/typescript/__tests__/__snapshots__/codeGeneration.ts.snap b/src/javascript/typescript/__tests__/__snapshots__/codeGeneration.ts.snap
index 62e7e9a4..f57240a0 100644
--- a/src/javascript/typescript/__tests__/__snapshots__/codeGeneration.ts.snap
+++ b/src/javascript/typescript/__tests__/__snapshots__/codeGeneration.ts.snap
@@ -45,6 +45,10 @@ export interface HeroName {
   hero: HeroName_hero | null;
 }
 
+export interface HeroNameVariables {
+  episode?: Episode | null;
+}
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -54,9 +58,9 @@ export interface HeroName {
 
 // The episodes in the Star Wars trilogy
 export enum Episode {
-  NEWHOPE = \\"NEWHOPE\\",
   EMPIRE = \\"EMPIRE\\",
   JEDI = \\"JEDI\\",
+  NEWHOPE = \\"NEWHOPE\\",
 }
 
 //==============================================================
@@ -87,9 +91,9 @@ export interface droidFragment {
 
 // The episodes in the Star Wars trilogy
 export enum Episode {
-  NEWHOPE = \\"NEWHOPE\\",
   EMPIRE = \\"EMPIRE\\",
   JEDI = \\"JEDI\\",
+  NEWHOPE = \\"NEWHOPE\\",
 }
 
 //==============================================================
@@ -133,9 +137,9 @@ export interface humanFragment {
 
 // The episodes in the Star Wars trilogy
 export enum Episode {
-  NEWHOPE = \\"NEWHOPE\\",
   EMPIRE = \\"EMPIRE\\",
   JEDI = \\"JEDI\\",
+  NEWHOPE = \\"NEWHOPE\\",
 }
 
 //==============================================================
@@ -239,9 +243,9 @@ export type anotherFragment = anotherFragment_Droid | anotherFragment_Human;
 
 // The episodes in the Star Wars trilogy
 export enum Episode {
-  NEWHOPE = \\"NEWHOPE\\",
   EMPIRE = \\"EMPIRE\\",
   JEDI = \\"JEDI\\",
+  NEWHOPE = \\"NEWHOPE\\",
 }
 
 //==============================================================
@@ -272,9 +276,9 @@ export interface simpleFragment {
 
 // The episodes in the Star Wars trilogy
 export enum Episode {
-  NEWHOPE = \\"NEWHOPE\\",
   EMPIRE = \\"EMPIRE\\",
   JEDI = \\"JEDI\\",
+  NEWHOPE = \\"NEWHOPE\\",
 }
 
 //==============================================================
@@ -341,6 +345,10 @@ export interface HeroInlineFragment {
   hero: HeroInlineFragment_hero | null;
 }
 
+export interface HeroInlineFragmentVariables {
+  episode?: Episode | null;
+}
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -350,9 +358,9 @@ export interface HeroInlineFragment {
 
 // The episodes in the Star Wars trilogy
 export enum Episode {
-  NEWHOPE = \\"NEWHOPE\\",
   EMPIRE = \\"EMPIRE\\",
   JEDI = \\"JEDI\\",
+  NEWHOPE = \\"NEWHOPE\\",
 }
 
 //==============================================================
@@ -400,6 +408,10 @@ export interface HeroName {
   hero: HeroName_hero | null;
 }
 
+export interface HeroNameVariables {
+  episode?: Episode | null;
+}
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -409,9 +421,9 @@ export interface HeroName {
 
 // The episodes in the Star Wars trilogy
 export enum Episode {
-  NEWHOPE = \\"NEWHOPE\\",
   EMPIRE = \\"EMPIRE\\",
   JEDI = \\"JEDI\\",
+  NEWHOPE = \\"NEWHOPE\\",
 }
 
 //==============================================================
@@ -465,6 +477,10 @@ export interface HeroName {
   hero: HeroName_hero | null;
 }
 
+export interface HeroNameVariables {
+  episode?: Episode | null;
+}
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -474,9 +490,9 @@ export interface HeroName {
 
 // The episodes in the Star Wars trilogy
 export enum Episode {
-  NEWHOPE = \\"NEWHOPE\\",
   EMPIRE = \\"EMPIRE\\",
   JEDI = \\"JEDI\\",
+  NEWHOPE = \\"NEWHOPE\\",
 }
 
 //==============================================================
@@ -509,6 +525,10 @@ export interface HeroName {
   hero: HeroName_hero | null;
 }
 
+export interface HeroNameVariables {
+  episode?: Episode | null;
+}
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -518,9 +538,9 @@ export interface HeroName {
 
 // The episodes in the Star Wars trilogy
 export enum Episode {
-  NEWHOPE = \\"NEWHOPE\\",
   EMPIRE = \\"EMPIRE\\",
   JEDI = \\"JEDI\\",
+  NEWHOPE = \\"NEWHOPE\\",
 }
 
 // The input object sent when someone is creating a new review
@@ -566,6 +586,10 @@ export interface SomeOther {
   hero: SomeOther_hero | null;
 }
 
+export interface SomeOtherVariables {
+  episode?: Episode | null;
+}
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -575,9 +599,9 @@ export interface SomeOther {
 
 // The episodes in the Star Wars trilogy
 export enum Episode {
-  NEWHOPE = \\"NEWHOPE\\",
   EMPIRE = \\"EMPIRE\\",
   JEDI = \\"JEDI\\",
+  NEWHOPE = \\"NEWHOPE\\",
 }
 
 // The input object sent when someone is creating a new review
@@ -623,6 +647,11 @@ export interface ReviewMovie {
   createReview: ReviewMovie_createReview | null;
 }
 
+export interface ReviewMovieVariables {
+  episode?: Episode | null;
+  review?: ReviewInput | null;
+}
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -632,9 +661,9 @@ export interface ReviewMovie {
 
 // The episodes in the Star Wars trilogy
 export enum Episode {
-  NEWHOPE = \\"NEWHOPE\\",
   EMPIRE = \\"EMPIRE\\",
   JEDI = \\"JEDI\\",
+  NEWHOPE = \\"NEWHOPE\\",
 }
 
 // The input object sent when someone is creating a new review
@@ -684,9 +713,9 @@ export interface someFragment {
 
 // The episodes in the Star Wars trilogy
 export enum Episode {
-  NEWHOPE = \\"NEWHOPE\\",
   EMPIRE = \\"EMPIRE\\",
   JEDI = \\"JEDI\\",
+  NEWHOPE = \\"NEWHOPE\\",
 }
 
 // The input object sent when someone is creating a new review
@@ -731,6 +760,10 @@ export interface HeroFragment {
   hero: HeroFragment_hero | null;
 }
 
+export interface HeroFragmentVariables {
+  episode?: Episode | null;
+}
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -740,9 +773,9 @@ export interface HeroFragment {
 
 // The episodes in the Star Wars trilogy
 export enum Episode {
-  NEWHOPE = \\"NEWHOPE\\",
   EMPIRE = \\"EMPIRE\\",
   JEDI = \\"JEDI\\",
+  NEWHOPE = \\"NEWHOPE\\",
 }
 
 //==============================================================
@@ -773,9 +806,9 @@ export interface simpleFragment {
 
 // The episodes in the Star Wars trilogy
 export enum Episode {
-  NEWHOPE = \\"NEWHOPE\\",
   EMPIRE = \\"EMPIRE\\",
   JEDI = \\"JEDI\\",
+  NEWHOPE = \\"NEWHOPE\\",
 }
 
 //==============================================================
@@ -838,6 +871,10 @@ export interface HeroName {
   hero: HeroName_hero | null;
 }
 
+export interface HeroNameVariables {
+  episode?: Episode | null;
+}
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -847,9 +884,9 @@ export interface HeroName {
 
 // The episodes in the Star Wars trilogy
 export enum Episode {
-  NEWHOPE = \\"NEWHOPE\\",
   EMPIRE = \\"EMPIRE\\",
   JEDI = \\"JEDI\\",
+  NEWHOPE = \\"NEWHOPE\\",
 }
 
 //==============================================================
@@ -881,6 +918,11 @@ export interface ReviewMovie {
   createReview: ReviewMovie_createReview | null;
 }
 
+export interface ReviewMovieVariables {
+  episode?: Episode | null;
+  review?: ReviewInput | null;
+}
+
 //==============================================================
 // START Enums and Input Objects
 // All enums and input objects are included in every output file
@@ -890,9 +932,9 @@ export interface ReviewMovie {
 
 // The episodes in the Star Wars trilogy
 export enum Episode {
-  NEWHOPE = \\"NEWHOPE\\",
   EMPIRE = \\"EMPIRE\\",
   JEDI = \\"JEDI\\",
+  NEWHOPE = \\"NEWHOPE\\",
 }
 
 // The input object sent when someone is creating a new review
diff --git a/test/flow/__snapshots__/codeGeneration.js.snap b/test/flow/__snapshots__/codeGeneration.js.snap
index 8bc16de1..d3e28fb8 100644
--- a/test/flow/__snapshots__/codeGeneration.js.snap
+++ b/test/flow/__snapshots__/codeGeneration.js.snap
@@ -34,9 +34,9 @@ exports[`Flow code generation #generateSource() should generate correct typedefs
 
 // The episodes in the Star Wars trilogy
 export type Episode =
-  \\"NEWHOPE\\" | // Star Wars Episode IV: A New Hope, released in 1977.
   \\"EMPIRE\\" | // Star Wars Episode V: The Empire Strikes Back, released in 1980.
-  \\"JEDI\\"; // Star Wars Episode VI: Return of the Jedi, released in 1983.
+  \\"JEDI\\" | // Star Wars Episode VI: Return of the Jedi, released in 1983.
+  \\"NEWHOPE\\"; // Star Wars Episode IV: A New Hope, released in 1977.
 
 
 export type HeroAndFriendsNamesQueryVariables = {|
@@ -108,9 +108,9 @@ exports[`Flow code generation #generateSource() should generate correct typedefs
 
 // The episodes in the Star Wars trilogy
 export type Episode =
-  \\"NEWHOPE\\" | // Star Wars Episode IV: A New Hope, released in 1977.
   \\"EMPIRE\\" | // Star Wars Episode V: The Empire Strikes Back, released in 1980.
-  \\"JEDI\\"; // Star Wars Episode VI: Return of the Jedi, released in 1983.
+  \\"JEDI\\" | // Star Wars Episode VI: Return of the Jedi, released in 1983.
+  \\"NEWHOPE\\"; // Star Wars Episode IV: A New Hope, released in 1977.
 
 
 export type HeroAndFriendsNamesQueryVariables = {|
@@ -241,9 +241,9 @@ exports[`Flow code generation #generateSource() should generate mutation operati
 
 // The episodes in the Star Wars trilogy
 export type Episode =
-  \\"NEWHOPE\\" | // Star Wars Episode IV: A New Hope, released in 1977.
   \\"EMPIRE\\" | // Star Wars Episode V: The Empire Strikes Back, released in 1980.
-  \\"JEDI\\"; // Star Wars Episode VI: Return of the Jedi, released in 1983.
+  \\"JEDI\\" | // Star Wars Episode VI: Return of the Jedi, released in 1983.
+  \\"NEWHOPE\\"; // Star Wars Episode IV: A New Hope, released in 1977.
 
 
 export type ReviewInput = {|
@@ -318,9 +318,9 @@ exports[`Flow code generation #generateSource() should generate simple nested qu
 
 // The episodes in the Star Wars trilogy
 export type Episode =
-  \\"NEWHOPE\\" | // Star Wars Episode IV: A New Hope, released in 1977.
   \\"EMPIRE\\" | // Star Wars Episode V: The Empire Strikes Back, released in 1980.
-  \\"JEDI\\"; // Star Wars Episode VI: Return of the Jedi, released in 1983.
+  \\"JEDI\\" | // Star Wars Episode VI: Return of the Jedi, released in 1983.
+  \\"NEWHOPE\\"; // Star Wars Episode IV: A New Hope, released in 1977.
 
 
 export type HeroAndFriendsNamesQueryVariables = {|
@@ -402,9 +402,9 @@ exports[`Flow code generation #generateSource() should generate simple query ope
 
 // The episodes in the Star Wars trilogy
 export type Episode =
-  \\"NEWHOPE\\" | // Star Wars Episode IV: A New Hope, released in 1977.
   \\"EMPIRE\\" | // Star Wars Episode V: The Empire Strikes Back, released in 1980.
-  \\"JEDI\\"; // Star Wars Episode VI: Return of the Jedi, released in 1983.
+  \\"JEDI\\" | // Star Wars Episode VI: Return of the Jedi, released in 1983.
+  \\"NEWHOPE\\"; // Star Wars Episode IV: A New Hope, released in 1977.
 
 
 export type HeroNameQueryVariables = {|