Skip to content

typescript-operations emits an unused schema-types import when fragment types are referenced #10922

Description

@wassim-mirus

Which packages are impacted by your issue?

@graphql-codegen/typescript-operations

Describe the bug

With importSchemaTypesFrom and inlineFragmentTypes: 'combine', a document that only spreads a
fragment gets an import type * as Types it never references. Under noUnusedLocals that fails the
build.

Reproduction

npm install
npm run generate
npm run typecheck
src/book.generated.ts(4,1): error TS6133: 'Types' is declared but its value is never read.

Two fragments, in separate files. Only Category selects the enum field:

# src/category.graphql
fragment Category on Category { id kind }   # kind: CategoryKind!

# src/book.graphql
fragment Book on Book { id category { ...Category } }

src/category.generated.ts — import used, correct:

import type * as Types from '../types';

export type CategoryFragment = { id: string, kind: Types.CategoryKind };

src/book.generated.ts — import emitted, never referenced:

import { CategoryFragment } from './category.generated';
import type * as Types from '../types';   // <-- unused

export type BookFragment = { id: string, category: CategoryFragment };

Expected

No Types import in src/book.generated.ts, since nothing in the file refers to it.

Cause

TypeScriptDocumentsVisitor.getExternalSchemaTypeImports()
(packages/plugins/typescript/operations/src/visitor.ts) gates the import on _usedSchemaTypes:

const hasTypesToImport =
  Object.values(this._usedSchemaTypes).filter(
    value => value.type === 'GraphQLEnumType' || value.type === 'GraphQLInputObjectType',
  ).length > 0;

if (!hasTypesToImport) {
  return [];
}

CategoryKind is used by the document, so this is true for both files. But used by the document
and named by the generated file are different, and only the second justifies an import.
_usedSchemaTypes cannot answer the second on its own: it also decides whether enum and input
definitions are generated locally, which must account for types reached through fragments.

Only inlineFragmentTypes decides which of the two applies:

inlineFragmentTypes BookFragment names Types? tsc
'inline' (default) { category: { id: string, kind: Types.CategoryKind } } yes passes
'combine' { category: CategoryFragment } no fails

Your Example Website or App

https://stackblitz.com/edit/gdtmg1pc

Steps to Reproduce the Bug or Issue

Run npm generate in the repro

Expected behavior

It should not have unused imports

Screenshots or Videos

No response

Platform

package version
@graphql-codegen/cli 7.2.0
@graphql-codegen/typescript-operations 6.1.6
@graphql-codegen/near-operation-file-preset 5.2.2
graphql 16.11.0
typescript 5.9.3

Codegen Config File

import type { CodegenConfig } from '@graphql-codegen/cli';

const config: CodegenConfig = {
  schema: './schema.graphql',
  documents: './src/**/*.graphql',
  config: {
    // Fragment types are referenced, not inlined. This is what makes the bug observable:
    // a document that only spreads a fragment never names the schema-types namespace itself.
    inlineFragmentTypes: 'combine',
  },
  generates: {
    // Shared Input/Enum types, per the v6 migration guide's multi-file setup.
    './types.ts': {
      plugins: ['typescript-operations'],
      config: {
        generateOperationTypes: false,
      },
    },
    // Per-document operation types, importing the shared types from the file above.
    './src': {
      preset: 'near-operation-file',
      presetConfig: {
        extension: '.generated.ts',
      },
      plugins: ['typescript-operations'],
      config: {
        importSchemaTypesFrom: './types.ts',
        namespacedImportName: 'Types',
      },
    },
  },
};

export default config;

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions