Repository: apollographql/apollo-codegen
Pull request #340 (2017-12-22 19:29:52)

Export input types

ecstasy2: This should address issue #320.

Patch:
diff --git a/src/javascript/flow/codeGeneration.ts b/src/javascript/flow/codeGeneration.ts
index f31c7a2a..7af01be5 100644
--- a/src/javascript/flow/codeGeneration.ts
+++ b/src/javascript/flow/codeGeneration.ts
@@ -138,7 +138,16 @@ export class FlowAPIGenerator extends FlowGenerator {
   }
 
   public typeAliasForInputObjectType(inputObjectType: GraphQLInputObjectType) {
-    this.printer.enqueue(this.inputObjectDeclaration(inputObjectType));
+    const typeAlias = this.inputObjectDeclaration(inputObjectType);
+
+    const { description } = inputObjectType;
+    const exportDeclarationOptions = description
+      ? { comments: ` ${description.replace('\n', ' ')}` }
+      : {};
+
+    const exportedTypeAlias = this.exportDeclaration(typeAlias, exportDeclarationOptions);
+    this.printer.enqueue(exportedTypeAlias);
+
   }
 
   public typeAliasesForOperation(operation: Operation) {
diff --git a/src/javascript/flow/language.ts b/src/javascript/flow/language.ts
index 3daae716..cc8868b9 100644
--- a/src/javascript/flow/language.ts
+++ b/src/javascript/flow/language.ts
@@ -58,7 +58,7 @@ export default class FlowGenerator {
   }
 
   public inputObjectDeclaration(inputObjectType: GraphQLInputObjectType) {
-    const { name, description } = inputObjectType;
+    const { name } = inputObjectType;
 
     const fieldMap = inputObjectType.getFields();
     const fields: ObjectProperty[] = Object.keys(inputObjectType.getFields())
@@ -74,13 +74,6 @@ export default class FlowGenerator {
       keyInheritsNullability: true
     });
 
-    if (description) {
-      typeAlias.leadingComments = [{
-        type: 'CommentLine',
-        value: ` ${description.replace('\n', ' ')}`
-      } as t.CommentLine]
-    }
-
     return typeAlias;
   }
 
@@ -155,8 +148,17 @@ export default class FlowGenerator {
     );
   }
 
-  public exportDeclaration(declaration: t.Declaration) {
-    return t.exportNamedDeclaration(declaration, []);
+  public exportDeclaration(declaration: t.Declaration, options: { comments?: string } = {}) {
+    const exportedDeclaration = t.exportNamedDeclaration(declaration, []);
+
+    if(options.comments) {
+      exportedDeclaration.leadingComments = [{
+        type: 'CommentLine',
+        value: options.comments,
+      } as t.CommentLine];
+    }
+
+    return exportedDeclaration;
   }
 
   public annotationFromScopeStack(scope: string[]) {
diff --git a/src/javascript/typescript/codeGeneration.ts b/src/javascript/typescript/codeGeneration.ts
index d10c6032..79c1dec3 100644
--- a/src/javascript/typescript/codeGeneration.ts
+++ b/src/javascript/typescript/codeGeneration.ts
@@ -3,7 +3,6 @@ import { stripIndent } from 'common-tags';
 import {
   GraphQLEnumType,
   GraphQLInputObjectType,
-  GraphQLNonNull,
 } from 'graphql';
 import * as path from 'path';
 
@@ -77,7 +76,7 @@ export function generateSource(
   Object.values(context.operations)
     .forEach((operation) => {
       generator.fileHeader();
-      generator.typeAliasesForOperation(operation);
+      generator.interfacesForOperation(operation);
       printEnumsAndInputObjects(generator, context);
 
       const output = generator.printer.printAndClear();
@@ -94,7 +93,7 @@ export function generateSource(
   Object.values(context.fragments)
     .forEach((fragment) => {
       generator.fileHeader();
-      generator.typeAliasesForFragment(fragment);
+      generator.interfacesForFragment(fragment);
       printEnumsAndInputObjects(generator, context);
 
       const output = generator.printer.printAndClear();
@@ -102,7 +101,7 @@ export function generateSource(
       const outputFilePath = path.join(
         path.dirname(fragment.filePath),
         '__generated__',
-        `${fragment.fragmentName}.js`
+        `${fragment.fragmentName}.ts`
       );
 
       generatedFiles[outputFilePath] = new TypescriptGeneratedFile(output);
@@ -141,7 +140,7 @@ export class TypescriptAPIGenerator extends TypescriptGenerator {
     this.printer.enqueue(this.inputObjectDeclaration(inputObjectType));
   }
 
-  public typeAliasesForOperation(operation: Operation) {
+  public interfacesForOperation(operation: Operation) {
     const {
       operationType,
       operationName,
@@ -164,19 +163,18 @@ export class TypescriptAPIGenerator extends TypescriptGenerator {
     const properties = this.getPropertiesForVariant(variant);
 
     const exportedTypeAlias = this.exportDeclaration(
-      this.typeAliasObject(operationName, properties)
+      this.interface(operationName, properties)
     );
 
     this.printer.enqueue(exportedTypeAlias);
     this.scopeStackPop();
   }
 
-  public typeAliasesForFragment(fragment: Fragment) {
+  public interfacesForFragment(fragment: Fragment) {
     const {
       fragmentName,
       selectionSet
     } = fragment;
-
     this.scopeStackPush(fragmentName);
 
     this.printer.enqueue(stripIndent`
@@ -190,9 +188,9 @@ export class TypescriptAPIGenerator extends TypescriptGenerator {
     if (variants.length === 1) {
       const properties = this.getPropertiesForVariant(variants[0]);
 
-      const name = this.annotationFromScopeStack(this.scopeStack).id.name;
+      const name = this.nameFromScopeStack(this.scopeStack);
       const exportedTypeAlias = this.exportDeclaration(
-        this.typeAliasObject(
+        this.interface(
           name,
           properties
         )
@@ -200,14 +198,14 @@ export class TypescriptAPIGenerator extends TypescriptGenerator {
 
       this.printer.enqueue(exportedTypeAlias);
     } else {
-      const unionMembers: t.FlowTypeAnnotation[] = [];
+      const unionMembers: t.Identifier[] = [];
       variants.forEach(variant => {
         this.scopeStackPush(variant.possibleTypes[0].toString());
         const properties = this.getPropertiesForVariant(variant);
 
-        const name = this.annotationFromScopeStack(this.scopeStack).id.name;
+        const name = this.nameFromScopeStack(this.scopeStack);
         const exportedTypeAlias = this.exportDeclaration(
-          this.typeAliasObject(
+          this.interface(
             name,
             properties
           )
@@ -215,7 +213,7 @@ export class TypescriptAPIGenerator extends TypescriptGenerator {
 
         this.printer.enqueue(exportedTypeAlias);
 
-        unionMembers.push(this.annotationFromScopeStack(this.scopeStack));
+        unionMembers.push(t.identifier(this.nameFromScopeStack(this.scopeStack)));
 
         this.scopeStackPop();
       });
@@ -223,8 +221,8 @@ export class TypescriptAPIGenerator extends TypescriptGenerator {
       this.printer.enqueue(
         this.exportDeclaration(
           this.typeAliasGenericUnion(
-            this.annotationFromScopeStack(this.scopeStack).id.name,
-            unionMembers
+            this.nameFromScopeStack(this.scopeStack),
+            unionMembers.map((id) => t.TSTypeReference(id))
           )
         )
       );
@@ -249,17 +247,14 @@ export class TypescriptAPIGenerator extends TypescriptGenerator {
       variant,
       this.context.options.mergeInFieldsFromFragmentSpreads
     );
-
     return fields.map(field => {
       const fieldName = field.alias !== undefined ? field.alias : field.name;
       this.scopeStackPush(fieldName);
 
-      let res;
+      let res: ObjectProperty;
       if (field.selectionSet) {
-        const genericAnnotation = this.annotationFromScopeStack(this.scopeStack);
-
         res = this.handleFieldSelectionSetValue(
-          genericAnnotation,
+          t.identifier(this.nameFromScopeStack(this.scopeStack)),
           field
         );
       } else {
@@ -274,7 +269,7 @@ export class TypescriptAPIGenerator extends TypescriptGenerator {
     });
   }
 
-  private handleFieldSelectionSetValue(generatedTypeAnnotation: t.GenericTypeAnnotation, field: Field) {
+  private handleFieldSelectionSetValue(generatedIdentifier: t.Identifier, field: Field): ObjectProperty {
     const { selectionSet } = field;
 
     const typeCase = this.getTypeCasesForSelectionSet(selectionSet as SelectionSet);
@@ -285,60 +280,66 @@ export class TypescriptAPIGenerator extends TypescriptGenerator {
       const variant = variants[0];
       const properties = this.getPropertiesForVariant(variant);
       exportedTypeAlias = this.exportDeclaration(
-        this.typeAliasObject(
-          this.annotationFromScopeStack(this.scopeStack).id.name,
+        this.interface(
+          this.nameFromScopeStack(this.scopeStack),
           properties
         )
       );
     } else {
-      const propertySets = variants.map(variant => {
+      const identifiers = variants.map(variant => {
         this.scopeStackPush(variant.possibleTypes[0].toString())
         const properties = this.getPropertiesForVariant(variant);
+        const identifierName = this.nameFromScopeStack(this.scopeStack);
+
+        this.printer.enqueue(this.exportDeclaration(
+          this.interface(
+            identifierName,
+            properties
+          )
+        ));
+
         this.scopeStackPop();
-        return properties;
-      })
+        return t.identifier(identifierName);
+      });
 
       exportedTypeAlias = this.exportDeclaration(
-        this.typeAliasObjectUnion(
-           generatedTypeAnnotation.id.name,
-          propertySets
+        this.typeAliasGenericUnion(
+          generatedIdentifier.name,
+          identifiers.map(i => t.TSTypeReference(i))
         )
       );
     }
 
     this.printer.enqueue(exportedTypeAlias);
 
-
     return {
       name: field.alias ? field.alias : field.name,
       description: field.description,
-      annotation: field.type instanceof GraphQLNonNull
-        ? generatedTypeAnnotation
-        : this.makeNullableAnnotation(generatedTypeAnnotation)
+      type: this.typeFromGraphQLType(field.type, {
+        replaceObjectTypeIdentifierWith: generatedIdentifier
+      })
     };
   }
 
-  private handleFieldValue(field: Field, variant: Variant) {
-    let res;
+  private handleFieldValue(field: Field, variant: Variant): ObjectProperty {
+    let res: ObjectProperty;
     if (field.name === '__typename') {
-      const annotations = variant.possibleTypes
+      const types = variant.possibleTypes
         .map(type => {
-          const annotation = t.stringLiteralTypeAnnotation();
-          annotation.value = type.toString();
-          return annotation;
+          return t.TSLiteralType(t.stringLiteral(type.toString()));
         });
 
       res = {
         name: field.alias ? field.alias : field.name,
         description: field.description,
-        annotation: t.unionTypeAnnotation(annotations)
+        type: t.TSUnionType(types)
       };
     } else {
       // TODO: Double check that this works
       res = {
         name: field.alias ? field.alias : field.name,
         description: field.description,
-        annotation: this.typeAnnotationFromGraphQLType(field.type)
+        type: this.typeFromGraphQLType(field.type)
       };
     }
 
diff --git a/src/javascript/typescript/helpers.ts b/src/javascript/typescript/helpers.ts
index 52f027b0..73a1d953 100644
--- a/src/javascript/typescript/helpers.ts
+++ b/src/javascript/typescript/helpers.ts
@@ -15,64 +15,72 @@ import * as t from '@babel/types';
 import { CompilerOptions } from '../../compiler';
 
 const builtInScalarMap = {
-  [GraphQLString.name]: t.stringTypeAnnotation(),
-  [GraphQLInt.name]: t.numberTypeAnnotation(),
-  [GraphQLFloat.name]: t.numberTypeAnnotation(),
-  [GraphQLBoolean.name]: t.booleanTypeAnnotation(),
-  [GraphQLID.name]: t.stringTypeAnnotation(),
+  [GraphQLString.name]: t.TSStringKeyword(),
+  [GraphQLInt.name]: t.TSNumberKeyword(),
+  [GraphQLFloat.name]: t.TSNumberKeyword(),
+  [GraphQLBoolean.name]: t.TSBooleanKeyword(),
+  [GraphQLID.name]: t.TSStringKeyword(),
 }
 
-export function createTypeAnnotationFromGraphQLTypeFunction(
+export interface TypeFromGraphQLTypeOptions {
+  replaceObjectTypeIdentifierWith?: t.Identifier;
+}
+
+export function createTypeFromGraphQLTypeFunction(
   compilerOptions: CompilerOptions
-): Function {
-  return function typeAnnotationFromGraphQLType(type: GraphQLType, {
-    nullable
+): (graphQLType: GraphQLType, options?: TypeFromGraphQLTypeOptions) => t.TSType {
+  return function typeFromGraphQLType(graphQLType: GraphQLType, {
+    nullable = true,
+    replaceObjectTypeIdentifierWith
+  }: {
+    nullable?: boolean;
+    replaceObjectTypeIdentifierWith?: t.Identifier
   } = {
     nullable: true
-  }): t.FlowTypeAnnotation {
-    if (type instanceof GraphQLNonNull) {
-      return typeAnnotationFromGraphQLType(
-        type.ofType,
-        { nullable: false }
+  }): t.TSType {
+    if (graphQLType instanceof GraphQLNonNull) {
+      return typeFromGraphQLType(
+        graphQLType.ofType,
+        { nullable: false, replaceObjectTypeIdentifierWith }
       );
     }
 
-    if (type instanceof GraphQLList) {
-      const typeAnnotation = t.arrayTypeAnnotation(
-        typeAnnotationFromGraphQLType(type.ofType)
+    if (graphQLType instanceof GraphQLList) {
+      const elementType = typeFromGraphQLType(graphQLType.ofType, { replaceObjectTypeIdentifierWith, nullable: true });
+      const type = t.TSArrayType(
+        t.isTSUnionType(elementType) ? t.TSParenthesizedType(elementType) : elementType
       );
-
       if (nullable) {
-        return t.nullableTypeAnnotation(typeAnnotation);
+        return t.TSUnionType([type, t.TSNullKeyword()]);
       } else {
-        return typeAnnotation;
+        return type;
       }
     }
 
-    let typeAnnotation;
-    if (type instanceof GraphQLScalarType) {
-      const builtIn = builtInScalarMap[type.name]
+    let type: t.TSType;
+    if (graphQLType instanceof GraphQLScalarType) {
+      const builtIn = builtInScalarMap[graphQLType.name]
       if (builtIn) {
-        typeAnnotation = builtIn;
+        type = builtIn;
       } else {
         if (compilerOptions.passthroughCustomScalars) {
-          typeAnnotation = t.anyTypeAnnotation();
+          type = t.TSAnyKeyword();
         } else {
-          typeAnnotation = t.genericTypeAnnotation(
-            t.identifier(type.name)
+          type = t.TSTypeReference(
+            t.identifier(graphQLType.name)
           );
         }
       }
     } else {
-      typeAnnotation = t.genericTypeAnnotation(
-        t.identifier(type.name)
+      type = t.TSTypeReference(
+        replaceObjectTypeIdentifierWith ? replaceObjectTypeIdentifierWith : t.identifier(graphQLType.name)
       );
     }
 
     if (nullable) {
-      return t.nullableTypeAnnotation(typeAnnotation);
+      return t.TSUnionType([type, t.TSNullKeyword()]);
     } else {
-      return typeAnnotation;
+      return type;
     }
   }
 }
diff --git a/src/javascript/typescript/language.ts b/src/javascript/typescript/language.ts
index 85eafd41..3eeaa8f7 100644
--- a/src/javascript/typescript/language.ts
+++ b/src/javascript/typescript/language.ts
@@ -1,20 +1,21 @@
 import {
   GraphQLEnumType,
   GraphQLInputObjectType,
+  GraphQLType
 } from 'graphql';
 
 import {
   CompilerOptions
 } from '../../compiler';
 
-import { createTypeAnnotationFromGraphQLTypeFunction } from './helpers';
+import { createTypeFromGraphQLTypeFunction, TypeFromGraphQLTypeOptions } from './helpers';
 
 import * as t from '@babel/types';
 
 export type ObjectProperty = {
   name: string,
   description?: string | null | undefined,
-  annotation: t.FlowTypeAnnotation
+  type: t.TSType
 }
 
 export interface TypescriptCompilerOptions extends CompilerOptions {
@@ -23,27 +24,25 @@ export interface TypescriptCompilerOptions extends CompilerOptions {
 
 export default class TypescriptGenerator {
   options: TypescriptCompilerOptions
-  typeAnnotationFromGraphQLType: Function
+  typeFromGraphQLType: (graphQLType: GraphQLType, options?: TypeFromGraphQLTypeOptions) => t.TSType
 
   constructor(compilerOptions: TypescriptCompilerOptions) {
     this.options = compilerOptions;
 
-    this.typeAnnotationFromGraphQLType = createTypeAnnotationFromGraphQLTypeFunction(compilerOptions);
+    this.typeFromGraphQLType = createTypeFromGraphQLTypeFunction(compilerOptions);
   }
 
   public enumerationDeclaration(type: GraphQLEnumType) {
     const { name, description } = type;
     const enumMembers = type.getValues().map(({ value }) => {
-      // @ts-ignore
-      return t.tSEnumMember(
+      return t.TSEnumMember(
         t.identifier(value),
         t.stringLiteral(value)
       );
     });
 
     const typeAlias = t.exportNamedDeclaration(
-      // @ts-ignore
-      t.tSEnumDeclaration(
+      t.TSEnumDeclaration(
         t.identifier(name),
         enumMembers
       ),
@@ -69,87 +68,76 @@ export default class TypescriptGenerator {
         const field = fieldMap[fieldName];
         return {
           name: fieldName,
-          annotation: this.typeAnnotationFromGraphQLType(field.type)
+          type: this.typeFromGraphQLType(field.type)
         }
       });
 
-    const typeAlias = this.typeAliasObject(name, fields, {
+    const inputType = this.interface(name, fields, {
       keyInheritsNullability: true
     });
 
-    typeAlias.leadingComments = [{
+    inputType.leadingComments = [{
       type: 'CommentLine',
       value: ` ${description}`
     } as t.CommentLine]
 
-    return typeAlias;
+    return inputType;
   }
 
-  public objectTypeAnnotation(fields: ObjectProperty[], {
+  public typesForProperties(fields: ObjectProperty[], {
     keyInheritsNullability = false
   } : {
     keyInheritsNullability?: boolean
   } = {}) {
-    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
-          ),
-          annotation.type === "NullableTypeAnnotation"
-            ? this.makeNullableAnnotation(annotation.typeAnnotation)
-            : annotation
-        );
-
-        if (description) {
-          objectTypeProperty.trailingComments = [{
-            type: 'CommentLine',
-            value: ` ${description.replace('\n', ' ')}`
-          } as t.CommentLine]
-        }
 
-        return objectTypeProperty;
-      })
-    );
+    return fields.map(({name, description, type}) => {
+      const propertySignatureType = t.TSPropertySignature(
+        t.identifier(
+          // Nullable fields on input objects do not have to be defined
+          // as well, so allow these fields to be "undefined"
+          (keyInheritsNullability && this.isNullableType(type))
+            ? name + '?'
+            : name
+        ),
+        t.TSTypeAnnotation(type)
+      );
+
+      if (description) {
+        propertySignatureType.trailingComments = [{
+          type: 'CommentLine',
+          value: ` ${description.replace('\n', ' ')}`
+        } as t.CommentLine]
+      }
 
-    return objectTypeAnnotation;
+      return propertySignatureType;
+    });
   }
 
-  public typeAliasObject(name: string, fields: ObjectProperty[], {
+  public interface(name: string, fields: ObjectProperty[], {
     keyInheritsNullability = false
   }: {
     keyInheritsNullability?: boolean
   } = {}) {
-    return t.typeAlias(
-      t.identifier(name),
-      undefined,
-      this.objectTypeAnnotation(fields, {
-        keyInheritsNullability
-      })
-    );
-  }
 
-  public typeAliasObjectUnion(name: string, members: ObjectProperty[][]) {
-    return t.typeAlias(
+    return t.TSInterfaceDeclaration(
       t.identifier(name),
       undefined,
-      t.unionTypeAnnotation(
-        members.map(member => {
-          return this.objectTypeAnnotation(member)
+      undefined,
+      t.TSInterfaceBody(
+        this.typesForProperties(fields, {
+          keyInheritsNullability
         })
       )
-    )
+    );
   }
 
-  public typeAliasGenericUnion(name: string, members: t.FlowTypeAnnotation[]) {
-    return t.typeAlias(
+  public typeAliasGenericUnion(name: string, members: t.TSType[]) {
+    return t.TSTypeAliasDeclaration(
       t.identifier(name),
       undefined,
-      t.unionTypeAnnotation(members)
+      t.TSUnionType(
+        members
+      )
     );
   }
 
@@ -157,21 +145,18 @@ export default class TypescriptGenerator {
     return t.exportNamedDeclaration(declaration, []);
   }
 
-  public annotationFromScopeStack(scope: string[]) {
-    return t.genericTypeAnnotation(
-      t.identifier(
-        scope.join('_')
-      )
-    );
+  public nameFromScopeStack(scope: string[]) {
+    return scope.join('_');
   }
 
-  public makeNullableAnnotation(annotation: t.FlowTypeAnnotation) {
-    return t.unionTypeAnnotation([
-      annotation,
-      // @ts-ignore
-      t.tSUndefinedKeyword(),
-      // @ts-ignore
-      t.tSNullKeyword()
+  public makeNullableType(type: t.TSType) {
+    return t.TSUnionType([
+      type,
+      t.TSNullKeyword()
     ])
   }
+
+  public isNullableType(type: t.TSType) {
+    return t.isTSUnionType(type) && type.types.some(type => t.isTSNullKeyword(type));
+  }
 }
diff --git a/src/javascript/typescript/printer.ts b/src/javascript/typescript/printer.ts
index 45845d79..f0ab5a77 100644
--- a/src/javascript/typescript/printer.ts
+++ b/src/javascript/typescript/printer.ts
@@ -1,8 +1,6 @@
 import * as t from '@babel/types';
 import generate from '@babel/generator';
 
-import { stripIndent } from 'common-tags';
-
 type Printable = t.Node | string;
 
 export default class Printer {
@@ -16,11 +14,11 @@ export default class Printer {
             return document + printable;
           } else {
             const documentPart = generate(printable).code;
-            return document + this.fixCommas(documentPart);
+            return document + this.indentComments(documentPart);
           }
         },
         ''
-      ) as string;
+      );
   }
 
   public enqueue(printable: Printable) {
@@ -38,55 +36,33 @@ export default class Printer {
     return output;
   }
 
-  /**
-   * When using trailing commas on ObjectTypeProperties within
-   * ObjectTypeAnnotations, we get weird behavior:
-   * ```
-   * {
-   *   homePlanet: ?string // description
-   *   ,
-   *   friends: any  // description
-   * }
-   * ```
-   * when we want
-   * ```
-   * {
-   *   homePlanet: ?string, // description
-   *   friends: any         // description
-   * }
-   * ```
-   */
-  private fixCommas(documentPart: string) {
+  private indentComments(documentPart: string) {
     const lines = documentPart
       .split('\n')
       .filter(Boolean);  // filter out lines that have no content
 
     let currentLine = 0;
-    let nextLine;
     const newDocumentParts = [];
     // Keep track of what column comments should start on
     // to keep things aligned
     let maxCommentColumn = 0;
 
     while (currentLine !== lines.length) {
-      nextLine = currentLine + 1;
-      const strippedNextLine = stripIndent`${lines[nextLine]}`;
-      if (strippedNextLine.length === 1 && strippedNextLine[0] === ',') {
-        const currentLineContents = lines[currentLine];
-        const commentColumn = currentLineContents.indexOf('//');
+      const currentLineContents = lines[currentLine];
+      const commentColumn = currentLineContents.indexOf('//');
+      if (commentColumn > 0) {
         if (maxCommentColumn < commentColumn) {
           maxCommentColumn = commentColumn;
         }
 
         const [contents, comment] = currentLineContents.split('//');
         newDocumentParts.push({
-          main: contents.replace(/\s+$/g, '') + ',',
-          comment: comment ? comment.trim(): null
+          main: contents.replace(/\s+$/g, ''),
+          comment: comment ? comment.trim() : null
         });
-        currentLine++;
       } else {
         newDocumentParts.push({
-          main: lines[currentLine],
+          main: currentLineContents,
           comment: null
         });
       }


Test patch:
diff --git a/src/javascript/flow/__tests__/__snapshots__/codeGeneration.ts.snap b/src/javascript/flow/__tests__/__snapshots__/codeGeneration.ts.snap
index e085e71a..894753a2 100644
--- a/src/javascript/flow/__tests__/__snapshots__/codeGeneration.ts.snap
+++ b/src/javascript/flow/__tests__/__snapshots__/codeGeneration.ts.snap
@@ -468,14 +468,14 @@ export type HeroName = {
 export type Episode = \\"NEWHOPE\\" | \\"EMPIRE\\" | \\"JEDI\\";
 
 // The input object sent when someone is creating a new review
-type ReviewInput = {
+export type ReviewInput = {
   stars: number,
   commentary?: ?string,
   favorite_color?: ?ColorInput,
 };
 
 // The input object sent when passing in a color
-type ColorInput = {
+export type ColorInput = {
   red: number,
   green: number,
   blue: number,
@@ -521,14 +521,14 @@ export type SomeOther = {
 export type Episode = \\"NEWHOPE\\" | \\"EMPIRE\\" | \\"JEDI\\";
 
 // The input object sent when someone is creating a new review
-type ReviewInput = {
+export type ReviewInput = {
   stars: number,
   commentary?: ?string,
   favorite_color?: ?ColorInput,
 };
 
 // The input object sent when passing in a color
-type ColorInput = {
+export type ColorInput = {
   red: number,
   green: number,
   blue: number,
@@ -574,14 +574,14 @@ export type ReviewMovie = {
 export type Episode = \\"NEWHOPE\\" | \\"EMPIRE\\" | \\"JEDI\\";
 
 // The input object sent when someone is creating a new review
-type ReviewInput = {
+export type ReviewInput = {
   stars: number,
   commentary?: ?string,
   favorite_color?: ?ColorInput,
 };
 
 // The input object sent when passing in a color
-type ColorInput = {
+export type ColorInput = {
   red: number,
   green: number,
   blue: number,
@@ -622,14 +622,14 @@ export type someFragment = {
 export type Episode = \\"NEWHOPE\\" | \\"EMPIRE\\" | \\"JEDI\\";
 
 // The input object sent when someone is creating a new review
-type ReviewInput = {
+export type ReviewInput = {
   stars: number,
   commentary?: ?string,
   favorite_color?: ?ColorInput,
 };
 
 // The input object sent when passing in a color
-type ColorInput = {
+export type ColorInput = {
   red: number,
   green: number,
   blue: number,
@@ -812,14 +812,14 @@ export type ReviewMovie = {
 export type Episode = \\"NEWHOPE\\" | \\"EMPIRE\\" | \\"JEDI\\";
 
 // The input object sent when someone is creating a new review
-type ReviewInput = {
+export type ReviewInput = {
   stars: number,
   commentary?: ?string,
   favorite_color?: ?ColorInput,
 };
 
 // The input object sent when passing in a color
-type ColorInput = {
+export type ColorInput = {
   red: number,
   green: number,
   blue: number,
diff --git a/src/javascript/typescript/__tests__/__snapshots__/codeGeneration.ts.snap b/src/javascript/typescript/__tests__/__snapshots__/codeGeneration.ts.snap
index d78f3e65..62e7e9a4 100644
--- a/src/javascript/typescript/__tests__/__snapshots__/codeGeneration.ts.snap
+++ b/src/javascript/typescript/__tests__/__snapshots__/codeGeneration.ts.snap
@@ -12,30 +12,38 @@ Object {
 // GraphQL query operation: HeroName
 // ====================================================
 
-export type HeroName_hero_Human_friends = {
-  __typename: \\"Human\\",
-  name: string, // What this human calls themselves
-} | {
-  __typename: \\"Droid\\",
-  id: string,   // The ID of the droid
-};
-
-export type HeroName_hero = {
-  __typename: \\"Human\\",
-  name: string,                                            // What this human calls themselves
-  id: string,                                              // The ID of the human
-  homePlanet: string | undefined | null,                   // The home planet of the human, or null if unknown
-  friends: HeroName_hero_Human_friends | undefined | null, // 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
-};
-
-export type HeroName = {
-  hero: HeroName_hero | undefined | null
-};
+export interface HeroName_hero_Human_friends_Human {
+  __typename: \\"Human\\";
+  name: string;  // What this human calls themselves
+}
+
+export interface HeroName_hero_Human_friends_Droid {
+  __typename: \\"Droid\\";
+  id: string;  // The ID of the droid
+}
+
+export type HeroName_hero_Human_friends = HeroName_hero_Human_friends_Human | HeroName_hero_Human_friends_Droid;
+
+export interface HeroName_hero_Human {
+  __typename: \\"Human\\";
+  name: string;                                            // What this human calls themselves
+  id: string;                                              // The ID of the human
+  homePlanet: string | null;                               // The home planet of the human, or null if unknown
+  friends: (HeroName_hero_Human_friends | null)[] | null;  // This human's friends, or an empty list if they have none
+}
+
+export interface HeroName_hero_Droid {
+  __typename: \\"Droid\\";
+  name: string;                   // What others call this droid
+  id: string;                     // The ID of the droid
+  appearsIn: (Episode | null)[];  // The movies this droid appears in
+}
+
+export type HeroName_hero = HeroName_hero_Human | HeroName_hero_Droid;
+
+export interface HeroName {
+  hero: HeroName_hero | null;
+}
 
 //==============================================================
 // START Enums and Input Objects
@@ -55,7 +63,7 @@ export enum Episode {
 // END Enums and Input Objects
 //==============================================================",
   },
-  "__generated__/droidFragment.js": TypescriptGeneratedFile {
+  "__generated__/droidFragment.ts": TypescriptGeneratedFile {
     "fileContents": "
 
 /* tslint:disable */
@@ -65,10 +73,10 @@ export enum Episode {
 // GraphQL fragment: droidFragment
 // ====================================================
 
-export type droidFragment = {
-  __typename: \\"Droid\\",
-  appearsIn: (?Episode)[], // The movies this droid appears in
-};
+export interface droidFragment {
+  __typename: \\"Droid\\";
+  appearsIn: (Episode | null)[];  // The movies this droid appears in
+}
 
 //==============================================================
 // START Enums and Input Objects
@@ -88,7 +96,7 @@ export enum Episode {
 // END Enums and Input Objects
 //==============================================================",
   },
-  "__generated__/humanFragment.js": TypescriptGeneratedFile {
+  "__generated__/humanFragment.ts": TypescriptGeneratedFile {
     "fileContents": "
 
 /* tslint:disable */
@@ -98,19 +106,23 @@ export enum Episode {
 // GraphQL fragment: humanFragment
 // ====================================================
 
-export type humanFragment_friends = {
-  __typename: \\"Human\\",
-  name: string, // What this human calls themselves
-} | {
-  __typename: \\"Droid\\",
-  id: string,   // The ID of the droid
-};
+export interface humanFragment_friends_Human {
+  __typename: \\"Human\\";
+  name: string;  // What this human calls themselves
+}
+
+export interface humanFragment_friends_Droid {
+  __typename: \\"Droid\\";
+  id: string;  // The ID of the droid
+}
+
+export type humanFragment_friends = humanFragment_friends_Human | humanFragment_friends_Droid;
 
-export type humanFragment = {
-  __typename: \\"Human\\",
-  homePlanet: string | undefined | null,             // The home planet of the human, or null if unknown
-  friends: humanFragment_friends | undefined | null, // This human's friends, or an empty list if they have none
-};
+export interface humanFragment {
+  __typename: \\"Human\\";
+  homePlanet: string | null;                         // The home planet of the human, or null if unknown
+  friends: (humanFragment_friends | null)[] | null;  // This human's friends, or an empty list if they have none
+}
 
 //==============================================================
 // START Enums and Input Objects
@@ -135,7 +147,7 @@ export enum Episode {
 
 exports[`Typescript codeGeneration fragment with fragment spreads 1`] = `
 Object {
-  "__generated__/anotherFragment.js": TypescriptGeneratedFile {
+  "__generated__/anotherFragment.ts": TypescriptGeneratedFile {
     "fileContents": "
 
 /* tslint:disable */
@@ -145,11 +157,11 @@ Object {
 // GraphQL fragment: anotherFragment
 // ====================================================
 
-export type anotherFragment = {
-  __typename: \\"Human\\" | \\"Droid\\",
-  id: string,   // The ID of the character
-  name: string, // The name of the character
-};
+export interface anotherFragment {
+  __typename: \\"Human\\" | \\"Droid\\";
+  id: string;    // The ID of the character
+  name: string;  // The name of the character
+}
 
 //==============================================================
 // START Enums and Input Objects
@@ -162,7 +174,7 @@ export type anotherFragment = {
 // END Enums and Input Objects
 //==============================================================",
   },
-  "__generated__/simpleFragment.js": TypescriptGeneratedFile {
+  "__generated__/simpleFragment.ts": TypescriptGeneratedFile {
     "fileContents": "
 
 /* tslint:disable */
@@ -172,10 +184,10 @@ export type anotherFragment = {
 // GraphQL fragment: simpleFragment
 // ====================================================
 
-export type simpleFragment = {
-  __typename: \\"Human\\" | \\"Droid\\",
-  name: string, // The name of the character
-};
+export interface simpleFragment {
+  __typename: \\"Human\\" | \\"Droid\\";
+  name: string;  // The name of the character
+}
 
 //==============================================================
 // START Enums and Input Objects
@@ -193,7 +205,7 @@ export type simpleFragment = {
 
 exports[`Typescript codeGeneration fragment with fragment spreads with inline fragment 1`] = `
 Object {
-  "__generated__/anotherFragment.js": TypescriptGeneratedFile {
+  "__generated__/anotherFragment.ts": TypescriptGeneratedFile {
     "fileContents": "
 
 /* tslint:disable */
@@ -203,18 +215,18 @@ Object {
 // GraphQL fragment: anotherFragment
 // ====================================================
 
-export type anotherFragment_Droid = {
-  __typename: \\"Droid\\",
-  id: string,   // The ID of the droid
-  name: string, // What others call this droid
-};
+export interface anotherFragment_Droid {
+  __typename: \\"Droid\\";
+  id: string;    // The ID of the droid
+  name: string;  // What others call this droid
+}
 
-export type anotherFragment_Human = {
-  __typename: \\"Human\\",
-  id: string,              // The ID of the human
-  name: string,            // What this human calls themselves
-  appearsIn: (?Episode)[], // The movies this human appears in
-};
+export interface anotherFragment_Human {
+  __typename: \\"Human\\";
+  id: string;                     // The ID of the human
+  name: string;                   // What this human calls themselves
+  appearsIn: (Episode | null)[];  // The movies this human appears in
+}
 
 export type anotherFragment = anotherFragment_Droid | anotherFragment_Human;
 
@@ -236,7 +248,7 @@ export enum Episode {
 // END Enums and Input Objects
 //==============================================================",
   },
-  "__generated__/simpleFragment.js": TypescriptGeneratedFile {
+  "__generated__/simpleFragment.ts": TypescriptGeneratedFile {
     "fileContents": "
 
 /* tslint:disable */
@@ -246,10 +258,10 @@ export enum Episode {
 // GraphQL fragment: simpleFragment
 // ====================================================
 
-export type simpleFragment = {
-  __typename: \\"Human\\" | \\"Droid\\",
-  name: string, // The name of the character
-};
+export interface simpleFragment {
+  __typename: \\"Human\\" | \\"Droid\\";
+  name: string;  // The name of the character
+}
 
 //==============================================================
 // START Enums and Input Objects
@@ -284,14 +296,14 @@ Object {
 // GraphQL query operation: CustomScalar
 // ====================================================
 
-export type CustomScalar_commentTest = {
-  __typename: \\"CommentTest\\",
-  multiLine: string | undefined | null, // This is a multi-line comment.
-};
+export interface CustomScalar_commentTest {
+  __typename: \\"CommentTest\\";
+  multiLine: string | null;  // This is a multi-line comment.
+}
 
-export type CustomScalar = {
-  commentTest: CustomScalar_commentTest | undefined | null
-};
+export interface CustomScalar {
+  commentTest: CustomScalar_commentTest | null;
+}
 
 //==============================================================
 // START Enums and Input Objects
@@ -319,15 +331,15 @@ Object {
 // GraphQL query operation: HeroInlineFragment
 // ====================================================
 
-export type HeroInlineFragment_hero = {
-  __typename: \\"Human\\" | \\"Droid\\",
-  name: string, // The name of the character
-  id: string,   // The ID of the character
-};
+export interface HeroInlineFragment_hero {
+  __typename: \\"Human\\" | \\"Droid\\";
+  name: string;  // The name of the character
+  id: string;    // The ID of the character
+}
 
-export type HeroInlineFragment = {
-  hero: HeroInlineFragment_hero | undefined | null
-};
+export interface HeroInlineFragment {
+  hero: HeroInlineFragment_hero | null;
+}
 
 //==============================================================
 // START Enums and Input Objects
@@ -362,27 +374,31 @@ Object {
 // GraphQL query operation: HeroName
 // ====================================================
 
-export type HeroName_hero_Human_friends = {
-  __typename: \\"Human\\" | \\"Droid\\",
-  name: string, // The name of the character
-};
+export interface HeroName_hero_Human_friends {
+  __typename: \\"Human\\" | \\"Droid\\";
+  name: string;  // The name of the character
+}
+
+export interface HeroName_hero_Human {
+  __typename: \\"Human\\";
+  name: string;                                            // What this human calls themselves
+  id: string;                                              // The ID of the human
+  homePlanet: string | null;                               // The home planet of the human, or null if unknown
+  friends: (HeroName_hero_Human_friends | null)[] | null;  // This human's friends, or an empty list if they have none
+}
+
+export interface HeroName_hero_Droid {
+  __typename: \\"Droid\\";
+  name: string;                   // What others call this droid
+  id: string;                     // The ID of the droid
+  appearsIn: (Episode | null)[];  // The movies this droid appears in
+}
 
-export type HeroName_hero = {
-  __typename: \\"Human\\",
-  name: string,                                            // What this human calls themselves
-  id: string,                                              // The ID of the human
-  homePlanet: string | undefined | null,                   // The home planet of the human, or null if unknown
-  friends: HeroName_hero_Human_friends | undefined | null, // 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
-};
+export type HeroName_hero = HeroName_hero_Human | HeroName_hero_Droid;
 
-export type HeroName = {
-  hero: HeroName_hero | undefined | null
-};
+export interface HeroName {
+  hero: HeroName_hero | null;
+}
 
 //==============================================================
 // START Enums and Input Objects
@@ -417,33 +433,37 @@ Object {
 // GraphQL query operation: HeroName
 // ====================================================
 
-export type HeroName_hero_Human_friends = {
-  __typename: \\"Human\\" | \\"Droid\\",
-  name: string, // The name of the character
-};
+export interface HeroName_hero_Human_friends {
+  __typename: \\"Human\\" | \\"Droid\\";
+  name: string;  // The name of the character
+}
+
+export interface HeroName_hero_Human {
+  __typename: \\"Human\\";
+  name: string;                                            // What this human calls themselves
+  id: string;                                              // The ID of the human
+  homePlanet: string | null;                               // The home planet of the human, or null if unknown
+  friends: (HeroName_hero_Human_friends | null)[] | null;  // This human's friends, or an empty list if they have none
+}
+
+export interface HeroName_hero_Droid_friends {
+  __typename: \\"Human\\" | \\"Droid\\";
+  id: string;  // The ID of the character
+}
 
-export type HeroName_hero_Droid_friends = {
-  __typename: \\"Human\\" | \\"Droid\\",
-  id: string, // The ID of the character
-};
+export interface HeroName_hero_Droid {
+  __typename: \\"Droid\\";
+  name: string;                                            // What others call this droid
+  id: string;                                              // The ID of the droid
+  appearsIn: (Episode | null)[];                           // The movies this droid appears in
+  friends: (HeroName_hero_Droid_friends | null)[] | null;  // This droid's friends, or an empty list if they have none
+}
 
-export type HeroName_hero = {
-  __typename: \\"Human\\",
-  name: string,                                            // What this human calls themselves
-  id: string,                                              // The ID of the human
-  homePlanet: string | undefined | null,                   // The home planet of the human, or null if unknown
-  friends: HeroName_hero_Human_friends | undefined | null, // 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 | undefined | null, // This droid's friends, or an empty list if they have none
-};
+export type HeroName_hero = HeroName_hero_Human | HeroName_hero_Droid;
 
-export type HeroName = {
-  hero: HeroName_hero | undefined | null
-};
+export interface HeroName {
+  hero: HeroName_hero | null;
+}
 
 //==============================================================
 // START Enums and Input Objects
@@ -479,15 +499,15 @@ TypescriptGeneratedFile {
 // GraphQL query operation: HeroName
 // ====================================================
 
-export type HeroName_hero = {
-  __typename: \\"Human\\" | \\"Droid\\",
-  name: string, // The name of the character
-  id: string,   // The ID of the character
-};
+export interface HeroName_hero {
+  __typename: \\"Human\\" | \\"Droid\\";
+  name: string;  // The name of the character
+  id: string;    // The ID of the character
+}
 
-export type HeroName = {
-  hero: HeroName_hero | undefined | null
-};
+export interface HeroName {
+  hero: HeroName_hero | null;
+}
 
 //==============================================================
 // START Enums and Input Objects
@@ -504,18 +524,18 @@ export enum Episode {
 }
 
 // The input object sent when someone is creating a new review
-type ReviewInput = {
-  stars: number,
-  commentary?: string | undefined | null,
-  favorite_color?: ColorInput | undefined | null,
-};
+interface ReviewInput {
+  stars: number;
+  commentary?: string | null;
+  favorite_color?: ColorInput | null;
+}
 
 // The input object sent when passing in a color
-type ColorInput = {
-  red: number,
-  green: number,
-  blue: number,
-};
+interface ColorInput {
+  red: number;
+  green: number;
+  blue: number;
+}
 
 //==============================================================
 // END Enums and Input Objects
@@ -536,15 +556,15 @@ TypescriptGeneratedFile {
 // GraphQL query operation: SomeOther
 // ====================================================
 
-export type SomeOther_hero = {
-  __typename: \\"Human\\" | \\"Droid\\",
-  name: string,            // The name of the character
-  appearsIn: (?Episode)[], // The movies this character appears in
-};
+export interface SomeOther_hero {
+  __typename: \\"Human\\" | \\"Droid\\";
+  name: string;                   // The name of the character
+  appearsIn: (Episode | null)[];  // The movies this character appears in
+}
 
-export type SomeOther = {
-  hero: SomeOther_hero | undefined | null
-};
+export interface SomeOther {
+  hero: SomeOther_hero | null;
+}
 
 //==============================================================
 // START Enums and Input Objects
@@ -561,18 +581,18 @@ export enum Episode {
 }
 
 // The input object sent when someone is creating a new review
-type ReviewInput = {
-  stars: number,
-  commentary?: string | undefined | null,
-  favorite_color?: ColorInput | undefined | null,
-};
+interface ReviewInput {
+  stars: number;
+  commentary?: string | null;
+  favorite_color?: ColorInput | null;
+}
 
 // The input object sent when passing in a color
-type ColorInput = {
-  red: number,
-  green: number,
-  blue: number,
-};
+interface ColorInput {
+  red: number;
+  green: number;
+  blue: number;
+}
 
 //==============================================================
 // END Enums and Input Objects
@@ -593,15 +613,15 @@ TypescriptGeneratedFile {
 // GraphQL mutation operation: ReviewMovie
 // ====================================================
 
-export type ReviewMovie_createReview = {
-  __typename: \\"Review\\",
-  stars: number,                         // The number of stars this review gave, 1-5
-  commentary: string | undefined | null, // Comment about the movie
-};
+export interface ReviewMovie_createReview {
+  __typename: \\"Review\\";
+  stars: number;              // The number of stars this review gave, 1-5
+  commentary: string | null;  // Comment about the movie
+}
 
-export type ReviewMovie = {
-  createReview: ReviewMovie_createReview | undefined | null
-};
+export interface ReviewMovie {
+  createReview: ReviewMovie_createReview | null;
+}
 
 //==============================================================
 // START Enums and Input Objects
@@ -618,18 +638,18 @@ export enum Episode {
 }
 
 // The input object sent when someone is creating a new review
-type ReviewInput = {
-  stars: number,
-  commentary?: string | undefined | null,
-  favorite_color?: ColorInput | undefined | null,
-};
+interface ReviewInput {
+  stars: number;
+  commentary?: string | null;
+  favorite_color?: ColorInput | null;
+}
 
 // The input object sent when passing in a color
-type ColorInput = {
-  red: number,
-  green: number,
-  blue: number,
-};
+interface ColorInput {
+  red: number;
+  green: number;
+  blue: number;
+}
 
 //==============================================================
 // END Enums and Input Objects
@@ -637,7 +657,7 @@ type ColorInput = {
 }
 `;
 
-exports[`Typescript codeGeneration multiple files 7`] = `"/some/file/__generated__/someFragment.js"`;
+exports[`Typescript codeGeneration multiple files 7`] = `"/some/file/__generated__/someFragment.ts"`;
 
 exports[`Typescript codeGeneration multiple files 8`] = `
 TypescriptGeneratedFile {
@@ -650,10 +670,10 @@ TypescriptGeneratedFile {
 // GraphQL fragment: someFragment
 // ====================================================
 
-export type someFragment = {
-  __typename: \\"Human\\" | \\"Droid\\",
-  appearsIn: (?Episode)[], // The movies this character appears in
-};
+export interface someFragment {
+  __typename: \\"Human\\" | \\"Droid\\";
+  appearsIn: (Episode | null)[];  // The movies this character appears in
+}
 
 //==============================================================
 // START Enums and Input Objects
@@ -670,18 +690,18 @@ export enum Episode {
 }
 
 // The input object sent when someone is creating a new review
-type ReviewInput = {
-  stars: number,
-  commentary?: string | undefined | null,
-  favorite_color?: ColorInput | undefined | null,
-};
+interface ReviewInput {
+  stars: number;
+  commentary?: string | null;
+  favorite_color?: ColorInput | null;
+}
 
 // The input object sent when passing in a color
-type ColorInput = {
-  red: number,
-  green: number,
-  blue: number,
-};
+interface ColorInput {
+  red: number;
+  green: number;
+  blue: number;
+}
 
 //==============================================================
 // END Enums and Input Objects
@@ -701,15 +721,15 @@ Object {
 // GraphQL query operation: HeroFragment
 // ====================================================
 
-export type HeroFragment_hero = {
-  __typename: \\"Human\\" | \\"Droid\\",
-  name: string, // The name of the character
-  id: string,   // The ID of the character
-};
+export interface HeroFragment_hero {
+  __typename: \\"Human\\" | \\"Droid\\";
+  name: string;  // The name of the character
+  id: string;    // The ID of the character
+}
 
-export type HeroFragment = {
-  hero: HeroFragment_hero | undefined | null
-};
+export interface HeroFragment {
+  hero: HeroFragment_hero | null;
+}
 
 //==============================================================
 // START Enums and Input Objects
@@ -729,7 +749,7 @@ export enum Episode {
 // END Enums and Input Objects
 //==============================================================",
   },
-  "__generated__/simpleFragment.js": TypescriptGeneratedFile {
+  "__generated__/simpleFragment.ts": TypescriptGeneratedFile {
     "fileContents": "
 
 /* tslint:disable */
@@ -739,10 +759,10 @@ export enum Episode {
 // GraphQL fragment: simpleFragment
 // ====================================================
 
-export type simpleFragment = {
-  __typename: \\"Human\\" | \\"Droid\\",
-  name: string, // The name of the character
-};
+export interface simpleFragment {
+  __typename: \\"Human\\" | \\"Droid\\";
+  name: string;  // The name of the character
+}
 
 //==============================================================
 // START Enums and Input Objects
@@ -767,7 +787,7 @@ export enum Episode {
 
 exports[`Typescript codeGeneration simple fragment 1`] = `
 Object {
-  "__generated__/SimpleFragment.js": TypescriptGeneratedFile {
+  "__generated__/SimpleFragment.ts": TypescriptGeneratedFile {
     "fileContents": "
 
 /* tslint:disable */
@@ -777,10 +797,10 @@ Object {
 // GraphQL fragment: SimpleFragment
 // ====================================================
 
-export type SimpleFragment = {
-  __typename: \\"Human\\" | \\"Droid\\",
-  name: string, // The name of the character
-};
+export interface SimpleFragment {
+  __typename: \\"Human\\" | \\"Droid\\";
+  name: string;  // The name of the character
+}
 
 //==============================================================
 // START Enums and Input Objects
@@ -808,15 +828,15 @@ Object {
 // GraphQL query operation: HeroName
 // ====================================================
 
-export type HeroName_hero = {
-  __typename: \\"Human\\" | \\"Droid\\",
-  name: string, // The name of the character
-  id: string,   // The ID of the character
-};
+export interface HeroName_hero {
+  __typename: \\"Human\\" | \\"Droid\\";
+  name: string;  // The name of the character
+  id: string;    // The ID of the character
+}
 
-export type HeroName = {
-  hero: HeroName_hero | undefined | null
-};
+export interface HeroName {
+  hero: HeroName_hero | null;
+}
 
 //==============================================================
 // START Enums and Input Objects
@@ -851,15 +871,15 @@ Object {
 // GraphQL mutation operation: ReviewMovie
 // ====================================================
 
-export type ReviewMovie_createReview = {
-  __typename: \\"Review\\",
-  stars: number,                         // The number of stars this review gave, 1-5
-  commentary: string | undefined | null, // Comment about the movie
-};
+export interface ReviewMovie_createReview {
+  __typename: \\"Review\\";
+  stars: number;              // The number of stars this review gave, 1-5
+  commentary: string | null;  // Comment about the movie
+}
 
-export type ReviewMovie = {
-  createReview: ReviewMovie_createReview | undefined | null
-};
+export interface ReviewMovie {
+  createReview: ReviewMovie_createReview | null;
+}
 
 //==============================================================
 // START Enums and Input Objects
@@ -876,18 +896,18 @@ export enum Episode {
 }
 
 // The input object sent when someone is creating a new review
-type ReviewInput = {
-  stars: number,
-  commentary?: string | undefined | null,
-  favorite_color?: ColorInput | undefined | null,
-};
+interface ReviewInput {
+  stars: number;
+  commentary?: string | null;
+  favorite_color?: ColorInput | null;
+}
 
 // The input object sent when passing in a color
-type ColorInput = {
-  red: number,
-  green: number,
-  blue: number,
-};
+interface ColorInput {
+  red: number;
+  green: number;
+  blue: number;
+}
 
 //==============================================================
 // END Enums and Input Objects
diff --git a/src/javascript/typescript/__tests__/helpers.ts b/src/javascript/typescript/__tests__/helpers.ts
index 6f8e757b..1542002e 100644
--- a/src/javascript/typescript/__tests__/helpers.ts
+++ b/src/javascript/typescript/__tests__/helpers.ts
@@ -9,100 +9,109 @@ import {
   GraphQLScalarType,
 } from 'graphql';
 
-import * as t from 'babel-types';
+import * as t from '@babel/types';
 
-import { createTypeAnnotationFromGraphQLTypeFunction } from '../helpers';
+import { createTypeFromGraphQLTypeFunction } from '../helpers';
 
-const typeAnnotationFromGraphQLType = createTypeAnnotationFromGraphQLTypeFunction({
+const typeFromGraphQLType = createTypeFromGraphQLTypeFunction({
   passthroughCustomScalars: false
 });
 
-describe('Flow typeAnnotationFromGraphQLType', () => {
+function nullableType(type: t.TSType) {
+  return t.TSUnionType([
+    type,
+    t.TSNullKeyword()
+  ])
+}
+
+describe('Typescript typeAnnotationFromGraphQLType', () => {
   test('String', () => {
-    expect(typeAnnotationFromGraphQLType(GraphQLString))
+    expect(typeFromGraphQLType(GraphQLString))
       .toMatchObject(
-        t.nullableTypeAnnotation(
-          t.stringTypeAnnotation()
+        nullableType(
+          t.TSStringKeyword()
         )
       );
   });
 
   test('Int', () => {
-    expect(typeAnnotationFromGraphQLType(GraphQLInt))
+    expect(typeFromGraphQLType(GraphQLInt))
       .toMatchObject(
-        t.nullableTypeAnnotation(
-          t.numberTypeAnnotation()
+        nullableType(
+          t.TSNumberKeyword()
         )
       );
   });
 
   test('Float', () => {
-    expect(typeAnnotationFromGraphQLType(GraphQLFloat))
+    expect(typeFromGraphQLType(GraphQLFloat))
       .toMatchObject(
-        t.nullableTypeAnnotation(
-          t.numberTypeAnnotation()
+        nullableType(
+          t.TSNumberKeyword()
         )
       );
   });
 
   test('Boolean', () => {
-    expect(typeAnnotationFromGraphQLType(GraphQLBoolean))
+    expect(typeFromGraphQLType(GraphQLBoolean))
       .toMatchObject(
-        t.nullableTypeAnnotation(
-          t.booleanTypeAnnotation()
+        nullableType(
+          t.TSBooleanKeyword()
         )
       );
   });
 
   test('ID', () => {
-    expect(typeAnnotationFromGraphQLType(GraphQLID))
+    expect(typeFromGraphQLType(GraphQLID))
       .toMatchObject(
-        t.nullableTypeAnnotation(
-          t.stringTypeAnnotation()
+        nullableType(
+          t.TSStringKeyword()
         )
       );
   });
 
   test('String!', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLNonNull(GraphQLString))
-    ).toMatchObject(t.stringTypeAnnotation());
+      typeFromGraphQLType(new GraphQLNonNull(GraphQLString))
+    ).toMatchObject(t.TSStringKeyword());
   });
 
   test('Int!', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLNonNull(GraphQLInt))
-    ).toMatchObject(t.numberTypeAnnotation());
+      typeFromGraphQLType(new GraphQLNonNull(GraphQLInt))
+    ).toMatchObject(t.TSNumberKeyword());
   });
 
   test('Float!', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLNonNull(GraphQLFloat))
-    ).toMatchObject(t.numberTypeAnnotation());
+      typeFromGraphQLType(new GraphQLNonNull(GraphQLFloat))
+    ).toMatchObject(t.TSNumberKeyword());
   });
 
   test('Boolean!', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLNonNull(GraphQLBoolean))
-    ).toMatchObject(t.booleanTypeAnnotation());
+      typeFromGraphQLType(new GraphQLNonNull(GraphQLBoolean))
+    ).toMatchObject(t.TSBooleanKeyword());
   });
 
   test('ID!', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLNonNull(GraphQLID))
-    ).toMatchObject(t.stringTypeAnnotation());
+      typeFromGraphQLType(new GraphQLNonNull(GraphQLID))
+    ).toMatchObject(t.TSStringKeyword());
   });
 
   // TODO: Test GenericTypeAnnotation
 
   test('[String]', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLList(GraphQLString))
+      typeFromGraphQLType(new GraphQLList(GraphQLString))
     ).toMatchObject(
-        t.nullableTypeAnnotation(
-          t.arrayTypeAnnotation(
-            t.nullableTypeAnnotation(
-              t.stringTypeAnnotation()
+        nullableType(
+          t.TSArrayType(
+            t.TSParenthesizedType(
+              nullableType(
+                t.TSStringKeyword()
+              )
             )
           )
         )
@@ -111,12 +120,14 @@ describe('Flow typeAnnotationFromGraphQLType', () => {
 
   test('[Int]', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLList(GraphQLInt))
+      typeFromGraphQLType(new GraphQLList(GraphQLInt))
     ).toMatchObject(
-        t.nullableTypeAnnotation(
-          t.arrayTypeAnnotation(
-            t.nullableTypeAnnotation(
-              t.numberTypeAnnotation()
+        nullableType(
+          t.TSArrayType(
+            t.TSParenthesizedType(
+              nullableType(
+                t.TSNumberKeyword()
+              )
             )
           )
         )
@@ -125,12 +136,14 @@ describe('Flow typeAnnotationFromGraphQLType', () => {
 
   test('[Float]', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLList(GraphQLFloat))
+      typeFromGraphQLType(new GraphQLList(GraphQLFloat))
     ).toMatchObject(
-        t.nullableTypeAnnotation(
-          t.arrayTypeAnnotation(
-            t.nullableTypeAnnotation(
-              t.numberTypeAnnotation()
+        nullableType(
+          t.TSArrayType(
+            t.TSParenthesizedType(
+              nullableType(
+                t.TSNumberKeyword()
+              )
             )
           )
         )
@@ -139,12 +152,14 @@ describe('Flow typeAnnotationFromGraphQLType', () => {
 
   test('[Boolean]', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLList(GraphQLBoolean))
+      typeFromGraphQLType(new GraphQLList(GraphQLBoolean))
     ).toMatchObject(
-        t.nullableTypeAnnotation(
-          t.arrayTypeAnnotation(
-            t.nullableTypeAnnotation(
-              t.booleanTypeAnnotation()
+        nullableType(
+          t.TSArrayType(
+            t.TSParenthesizedType(
+              nullableType(
+                t.TSBooleanKeyword()
+              )
             )
           )
         )
@@ -153,12 +168,14 @@ describe('Flow typeAnnotationFromGraphQLType', () => {
 
   test('[ID]', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLList(GraphQLID))
+      typeFromGraphQLType(new GraphQLList(GraphQLID))
     ).toMatchObject(
-        t.nullableTypeAnnotation(
-          t.arrayTypeAnnotation(
-            t.nullableTypeAnnotation(
-              t.stringTypeAnnotation()
+        nullableType(
+          t.TSArrayType(
+            t.TSParenthesizedType(
+              nullableType(
+                t.TSStringKeyword()
+              )
             )
           )
         )
@@ -167,11 +184,13 @@ describe('Flow typeAnnotationFromGraphQLType', () => {
 
   test('[String]!', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(GraphQLString)))
+      typeFromGraphQLType(new GraphQLNonNull(new GraphQLList(GraphQLString)))
     ).toMatchObject(
-        t.arrayTypeAnnotation(
-          t.nullableTypeAnnotation(
-            t.stringTypeAnnotation()
+        t.TSArrayType(
+          t.TSParenthesizedType(
+            nullableType(
+              t.TSStringKeyword()
+            )
           )
         )
       )
@@ -179,22 +198,26 @@ describe('Flow typeAnnotationFromGraphQLType', () => {
 
   test('[Int]!', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(GraphQLInt)))
+      typeFromGraphQLType(new GraphQLNonNull(new GraphQLList(GraphQLInt)))
     ).toMatchObject(
-        t.arrayTypeAnnotation(
-          t.nullableTypeAnnotation(
-            t.numberTypeAnnotation()
+        t.TSArrayType(
+          t.TSParenthesizedType(
+            nullableType(
+              t.TSNumberKeyword()
+            )
           )
         )
       )
   });
   test('[Float]!', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(GraphQLFloat)))
+      typeFromGraphQLType(new GraphQLNonNull(new GraphQLList(GraphQLFloat)))
     ).toMatchObject(
-        t.arrayTypeAnnotation(
-          t.nullableTypeAnnotation(
-            t.numberTypeAnnotation()
+        t.TSArrayType(
+          t.TSParenthesizedType(
+            nullableType(
+              t.TSNumberKeyword()
+            )
           )
         )
       )
@@ -202,11 +225,13 @@ describe('Flow typeAnnotationFromGraphQLType', () => {
 
   test('[Boolean]!', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(GraphQLBoolean)))
+      typeFromGraphQLType(new GraphQLNonNull(new GraphQLList(GraphQLBoolean)))
     ).toMatchObject(
-        t.arrayTypeAnnotation(
-          t.nullableTypeAnnotation(
-            t.booleanTypeAnnotation()
+        t.TSArrayType(
+          t.TSParenthesizedType(
+            nullableType(
+              t.TSBooleanKeyword()
+            )
           )
         )
       )
@@ -214,11 +239,13 @@ describe('Flow typeAnnotationFromGraphQLType', () => {
 
   test('[ID]!', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(GraphQLID)))
+      typeFromGraphQLType(new GraphQLNonNull(new GraphQLList(GraphQLID)))
     ).toMatchObject(
-        t.arrayTypeAnnotation(
-          t.nullableTypeAnnotation(
-            t.stringTypeAnnotation()
+        t.TSArrayType(
+          t.TSParenthesizedType(
+            nullableType(
+              t.TSStringKeyword()
+            )
           )
         )
       )
@@ -226,11 +253,11 @@ describe('Flow typeAnnotationFromGraphQLType', () => {
 
   test('[String!]', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLList(new GraphQLNonNull(GraphQLString)))
+      typeFromGraphQLType(new GraphQLList(new GraphQLNonNull(GraphQLString)))
     ).toMatchObject(
-      t.nullableTypeAnnotation(
-          t.arrayTypeAnnotation(
-            t.stringTypeAnnotation()
+      nullableType(
+          t.TSArrayType(
+            t.TSStringKeyword()
           )
         )
       )
@@ -238,11 +265,11 @@ describe('Flow typeAnnotationFromGraphQLType', () => {
 
   test('[Int!]', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLList(new GraphQLNonNull((GraphQLInt))))
+      typeFromGraphQLType(new GraphQLList(new GraphQLNonNull((GraphQLInt))))
     ).toMatchObject(
-      t.nullableTypeAnnotation(
-          t.arrayTypeAnnotation(
-            t.numberTypeAnnotation()
+      nullableType(
+          t.TSArrayType(
+            t.TSNumberKeyword()
           )
         )
       )
@@ -250,11 +277,11 @@ describe('Flow typeAnnotationFromGraphQLType', () => {
 
   test('[Float!]', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLList(new GraphQLNonNull(GraphQLFloat)))
+      typeFromGraphQLType(new GraphQLList(new GraphQLNonNull(GraphQLFloat)))
     ).toMatchObject(
-        t.nullableTypeAnnotation(
-          t.arrayTypeAnnotation(
-            t.numberTypeAnnotation()
+        nullableType(
+          t.TSArrayType(
+            t.TSNumberKeyword()
           )
         )
       )
@@ -262,11 +289,11 @@ describe('Flow typeAnnotationFromGraphQLType', () => {
 
   test('[Boolean!]', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLList(new GraphQLNonNull(GraphQLBoolean)))
+      typeFromGraphQLType(new GraphQLList(new GraphQLNonNull(GraphQLBoolean)))
     ).toMatchObject(
-        t.nullableTypeAnnotation(
-          t.arrayTypeAnnotation(
-            t.booleanTypeAnnotation()
+        nullableType(
+          t.TSArrayType(
+            t.TSBooleanKeyword()
           )
         )
       )
@@ -274,11 +301,11 @@ describe('Flow typeAnnotationFromGraphQLType', () => {
 
   test('[ID!]', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLList(new GraphQLNonNull(GraphQLID)))
+      typeFromGraphQLType(new GraphQLList(new GraphQLNonNull(GraphQLID)))
     ).toMatchObject(
-        t.nullableTypeAnnotation(
-          t.arrayTypeAnnotation(
-            t.stringTypeAnnotation()
+        nullableType(
+          t.TSArrayType(
+            t.TSStringKeyword()
           )
         )
       )
@@ -286,64 +313,68 @@ describe('Flow typeAnnotationFromGraphQLType', () => {
 
   test('[String!]!', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLString))))
+      typeFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLString))))
     ).toMatchObject(
-        t.arrayTypeAnnotation(
-          t.stringTypeAnnotation()
+        t.TSArrayType(
+          t.TSStringKeyword()
         )
       )
   });
 
   test('[Int!]!', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLInt))))
+      typeFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLInt))))
     ).toMatchObject(
-        t.arrayTypeAnnotation(
-          t.numberTypeAnnotation()
+        t.TSArrayType(
+          t.TSNumberKeyword()
         )
       )
   });
 
   test('[Float!]!', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLFloat))))
+      typeFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLFloat))))
     ).toMatchObject(
-        t.arrayTypeAnnotation(
-          t.numberTypeAnnotation()
+        t.TSArrayType(
+          t.TSNumberKeyword()
         )
       )
   });
 
   test('[Boolean!]!', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLBoolean))))
+      typeFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLBoolean))))
     ).toMatchObject(
-        t.arrayTypeAnnotation(
-          t.booleanTypeAnnotation()
+        t.TSArrayType(
+          t.TSBooleanKeyword()
         )
       )
   });
 
   test('[ID!]!', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLID))))
+      typeFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLID))))
     ).toMatchObject(
-        t.arrayTypeAnnotation(
-          t.stringTypeAnnotation()
+        t.TSArrayType(
+          t.TSStringKeyword()
         )
       )
   });
 
   test('[[String]]', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLList(new GraphQLList(GraphQLString)))
+      typeFromGraphQLType(new GraphQLList(new GraphQLList(GraphQLString)))
     ).toMatchObject(
-        t.nullableTypeAnnotation(
-          t.arrayTypeAnnotation(
-            t.nullableTypeAnnotation(
-              t.arrayTypeAnnotation(
-                t.nullableTypeAnnotation(
-                  t.stringTypeAnnotation()
+        nullableType(
+          t.TSArrayType(
+            t.TSParenthesizedType(
+              nullableType(
+                t.TSArrayType(
+                  t.TSParenthesizedType(
+                    nullableType(
+                      t.TSStringKeyword()
+                    )
+                  )
                 )
               )
             )
@@ -354,13 +385,17 @@ describe('Flow typeAnnotationFromGraphQLType', () => {
 
   test('[[String]]!', () => {
     expect(
-      typeAnnotationFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLList(GraphQLString))))
+      typeFromGraphQLType(new GraphQLNonNull(new GraphQLList(new GraphQLList(GraphQLString))))
     ).toMatchObject(
-        t.arrayTypeAnnotation(
-          t.nullableTypeAnnotation(
-            t.arrayTypeAnnotation(
-              t.nullableTypeAnnotation(
-                t.stringTypeAnnotation()
+        t.TSArrayType(
+          t.TSParenthesizedType(
+            nullableType(
+              t.TSArrayType(
+                t.TSParenthesizedType(
+                  nullableType(
+                    t.TSStringKeyword()
+                  )
+                )
               )
             )
           )
@@ -377,10 +412,10 @@ describe('Flow typeAnnotationFromGraphQLType', () => {
     });
 
     expect(
-      typeAnnotationFromGraphQLType(OddType)
+      typeFromGraphQLType(OddType)
     ).toMatchObject(
-      t.nullableTypeAnnotation(
-        t.genericTypeAnnotation(t.identifier('Odd'))
+      nullableType(
+        t.TSTypeReference(t.identifier('Odd'))
       )
     )
   });
@@ -390,7 +425,7 @@ describe('passthrough custom scalars', () => {
   let getTypeAnnotation: Function;
 
   beforeAll(() => {
-    getTypeAnnotation = createTypeAnnotationFromGraphQLTypeFunction({
+    getTypeAnnotation = createTypeFromGraphQLTypeFunction({
       passthroughCustomScalars: true
     });
   });
@@ -406,8 +441,8 @@ describe('passthrough custom scalars', () => {
     expect(
       getTypeAnnotation(OddType)
     ).toMatchObject(
-      t.nullableTypeAnnotation(
-        t.anyTypeAnnotation()
+      nullableType(
+        t.TSAnyKeyword()
       )
     )
   });