Graphql-code-generator: How to generate TS Types where all fields are optional (nullable)

Created on 28 Feb 2018  路  4Comments  路  Source: dotansimha/graphql-code-generator

I am attempting to generate types to use inside my graphQl server to make my resolvers easier to maintain. I hoping that I can do that using this project.

I am running into an issue with how optional/mandatory fields are generated. I am hoping that someone can suggest a workaround for me.

Schema:

type Query {
    people(): [Person!]!
}

# FirstName, LastName & BirthDate are manditory.  MiddleName is optional.
Person {
    firstName: String!
    middleName: String
    lastName: String!
    birthDate: BirthDate!
}

BirthDate {
    ...
}

When I generate a typescript class for this object
($(npm bin)/gql-gen --file schema.json --template typescript), I get the following:

export interface Person {
    firstName: String
    middleName?: String
    lastName: String
    birthDate: BirthDate
}
export interface Query {
    ...
}
export interface BirthDate {
    ...
}

Unfortunately, these objects are not extremely useful to me inside my GraphQl resolvers. Inside my resolvers, I populate fields lazily. What I would like to be generated is the following:

export interface Person {
    firstName?: String
    middleName?: String
    lastName?: String
    birthDate?: BirthDate
}
export interface Query {
    ...
}
export interface BirthDate {
    ...
}

To provide complete context, this is an example resolver. You can see that I am getting a compiler error here:

const personResolver: IFieldResolver<any, any> = (parentObject: any,
                                                  args: any,
                                                  context: any,
                                                  info: GraphQLResolveInfo): Promise<Person[]> => {
    const firstName: string = loadFirstNameFromDb(..);
    const lastName: string = loadlastNameFromDb(..);
    const middleName: string|undefined = loadMiddleNameFromDb(..);

    # NOTE: We have a separate resolver for BirthDate.  Birthdate is 
    # not known here.
    #
    # This causes the following TS Error: 
    # 
    #     Error:(7, 15) TS2322: Type '...' is not assignable to type 'Person'.
    #       Property 'birthDate' is missing in type '...'.
    const person: Person = {
        firstName: firstName,
        middleName: middleName,
        lastName: lastName,
    };
    return person
};

const siteResolver: IFieldResolver<any, any> = (parentObject: Person,
                                                args: any,
                                                context: any,
                                                info: GraphQLResolveInfo): Promise<BirthDate> => {
    ...
};

const resolvers: IResolvers = {
    Query: {
        ...
    },
    Person: 
    Order: {
        reports: () => [],
        studies: () => [],
        site: siteResolver,
    },
};

I have read through the other threads on this subject, but am still not sure if my use case is valid. Other threads:

  • #155 - TS and Nullable types, again
  • #138 - Nullable fields with typescript

Thanks!

Most helpful comment

Yes you can you Partial<SomeModel> but what you can do if you have nested long object?
For instance:

interface Post {
  id: number,
  creator?: Maybe<Creator>
  latestComment?: Maybe<Comment>
}

interface Creator {
  id: number,
  firstName: string
  email: string,
  createdAt: string,
  updatedAt: string,
}

interface Comment {
  id: number,
  creator: Maybe<Creator>,
  body: string,
  dateTime: string
}

Let's say from GraphQL server I want only few fields from creator

I'm writing tests for my component that could show post. That component have @Input or prop (angular/react) that represent one post.

In test I mocked my post object with only necessary fields for my component, But I got typescript error Field createdAt is missing from Creator type...

So my suggestion is adding config option for generate all field optional.

All 4 comments

You can always use Partial<Person> to turn all those fields as optional when you need to. I don't think it makes sense change this on a generator side as in your schema you clearly have requirement flag for those fields. Or you can make your own templates I guess :)

@FredyC is right, use Partial<Person> to achieve this functionality.

Yes you can you Partial<SomeModel> but what you can do if you have nested long object?
For instance:

interface Post {
  id: number,
  creator?: Maybe<Creator>
  latestComment?: Maybe<Comment>
}

interface Creator {
  id: number,
  firstName: string
  email: string,
  createdAt: string,
  updatedAt: string,
}

interface Comment {
  id: number,
  creator: Maybe<Creator>,
  body: string,
  dateTime: string
}

Let's say from GraphQL server I want only few fields from creator

I'm writing tests for my component that could show post. That component have @Input or prop (angular/react) that represent one post.

In test I mocked my post object with only necessary fields for my component, But I got typescript error Field createdAt is missing from Creator type...

So my suggestion is adding config option for generate all field optional.

This would also be useful for creating mock resolvers for use with graphql-tools' makeExecutableSchema.

Was this page helpful?
0 / 5 - 0 ratings