Graphql-code-generator: `mappers` option results in duplicate identifiers in the generated TS file

Created on 11 Nov 2019  路  20Comments  路  Source: dotansimha/graphql-code-generator

having some types remapped using the mappers option leaves both the new (imported) type and the old (generated) type in the generated file.

apparently it wasn't an issue before, but now the new Typescript does not like that and I get

TS2440: Import declaration conflicts with local declaration
enhancement plugins waiting-for-release

Most helpful comment

Is there maybe an option to automagically alias every mapper?

So for example a config like;

config:
  mapperTypeSuffix: Model
  mappers:
    Product: ./services/product/models#Product

Would generate an import like;

import { Product as ProductModel } from './services/product/models';

All 20 comments

@helios1138 Could you share your codegen.yml file?

@ardatan

overwrite: true
schema: src/graphql/**/*.graphql
documents: null
generates:
  src/types.ts:
    config:
      namingConvention:
        enumValues: keep
      scalars:
        Date: Date
        ID: ID<Node['__typename']>
        Upload: Promise<FileUpload>
      mappers:
        Node: ./graphql/resolvers#Node
        User: ./services/Users#User
        Thread: ./services/Threads#Thread
        # more types here...
      contextType: ./graphql/Context#Context
    plugins:
      - add:
          content:
            - import { ID } from './lib/idHelpers'
            - import { Connection } from 'core/src/Mongodb/pagination/getConnection'
            - import { FileUpload } from 'core/src/graphqlUpload'
      - typescript
      - typescript-resolvers

and so in the same file I get both the imported types from mappers, e.g.

import { Thread } from './services/Threads'

and the default generated types, e.g.

export type Thread = Node & {
  __typename?: 'Thread'
  id: Scalars['ID']
  title: Scalars['String']
  // more fields here...
}

@ardatan I assume it's because typescript plugin does not know about typescript-resolvers' mappers option, so it doesn't know to ignore them. Is there a way to manually skip/exclude some types from being generated?

or is it possible to somehow alias imported types, so that there is no conflict? like

mappers:
  Node: ./graphql/resolvers#Node as NodeRecord
  User: ./services/Users#User as UserRecord
  Thread: ./services/Threads#Thread as ThreadRecord

I am hitting this issue as well, when upgrading to TypeScript 3.7.2

The as X would be helpful or something like import * as Something from "./something".

@helios1138 @janhartmann You can try to import those like below;

mappers:
        Node: import('./graphql/resolvers').Node
        User: import('./services/Users').User
        Thread: import('./services/Threads').Thread

That works! Generated code in my case is:

MachineCluster: ResolverTypeWrapper<import('../api/location/LocationApi.swagger.generated').MachineCluster>,

@ardatan this does indeed work, thanks. Though it looks more like a workaround since it makes the config a bit harder to read and results in duplicates, for example, if generics are also used

Thread: import('./services/Threads').Thread
ThreadConnection: Connection<import('./services/Threads').Thread>

In any case, if import as is not an option at this moment, I think it would be good if the workaround you mentioned would be added to the docs since I believe it's a pretty common case when database types and graphql types have the same name.

Currently, as far as I see, the docs only mention path#Name way of importing

Heya, we also started facing this issue when I tried to upgrade to latest TypeScript (3.7.2). This breaking change is acknowledged in the TypeScript release notes: http://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#local-and-imported-type-declarations-now-conflict

Working around this by sticking with [email protected] for now.

In our codebase, pretty much every mapper has the same name as the resulting GraphQL type, and I agree that that definitely seems like a very common use case.

Cheers! :)

@lachenmayer Could you try using aliases with the following canary version?
1.8.4-alpha-d88e5f9a.53+d88e5f9a

Thanks so much for this @ardatan, will give this a try on Monday! :) As far as I can tell, this is the only thing blocking us from switching to TypeScript 3.7, with the sweet new optional?.chaining feature (so excited to try it out!).

Just to be clear, will I have to manually annotate the types in the mappers, eg. having to do Event: '../../log#Event as EventInternal, or will the types be automatically namespaced/qualified so that I don't have to change the mappers in my codegen.yml file? Would be amazing if the latter is the case!

Fixed in 1.9.0

Hi, I'm not sure this actually works - or maybe I'm just missing something.. I'm on version 1.9.1. Let's say I have the following structure:

// src/orm/entities/user.ts
export class User {
  readonly id: string;
  readonly name: string;
}
# src/graphql/users/schema.graphql
type User {
  id: ID!
  name: String!
}

type Query {
  users: [User!]!
}
# codegen.yml
generates:
  src/graphql/types.ts:
    schema: src/graphql/users/schema.graphql
    plugins:
      - typescript
      - typescript-resolvers
    config:
      mappers:
        User: 'src/orm/entities/user#User as UserEntity'

This results in the following line at the beginning of the generated types.ts:

import { UserEntity } from '../orm/entities/user';

The User as bit of the import seems to be somehow lost in translation.. which makes the result quite broken. Any idea what am I doing wrong? Pretty please, with cherry on top?

I'm seeing the same as @jahudka on v1.9.1, the result is import { UserRecord } from '../User; rather than import { User as UserRecord } from '../User. 馃

PR #2957 seems to do the correct job as it's outputting the correct information.

{
  default: false,
  isExternal: true,
  source: '../modules/users/model',
  type: 'UserRecord',
  import: 'User as UserRecord'
}

But somehow, the actual file output does not contain the alias:

import { UserRecord } from '../modules/users/model';

This doesn't seem to be working with default exports.

If I put this in my YAML:

config:
  mappers:
    User: "../api/models/user.model#default as UserModel"

I get this:

import { UserModel } from '../api/models/user.model';

Note that the default import works correctly if I omit as UserModel from the config.

@schmod Looks like there has not been a release cut since the fix was merged.

Available in v1.10.0.

@dotansimha This issue was about the same problem as I was talking here #2783. Glad to see it was sorted out. :smiley:

Is there maybe an option to automagically alias every mapper?

So for example a config like;

config:
  mapperTypeSuffix: Model
  mappers:
    Product: ./services/product/models#Product

Would generate an import like;

import { Product as ProductModel } from './services/product/models';
Was this page helpful?
0 / 5 - 0 ratings