Skip to content

fix(graphql): keep distinct GraphQL types with identical shape separate#3044

Open
schani wants to merge 3 commits into
masterfrom
agent/fix-issue-1072
Open

fix(graphql): keep distinct GraphQL types with identical shape separate#3044
schani wants to merge 3 commits into
masterfrom
agent/fix-issue-1072

Conversation

@schani

@schani schani commented Jul 20, 2026

Copy link
Copy Markdown
Member

Bug

Distinct GraphQL object types with identical structure were collapsed into a single output class. For example:

type GridConfig {
  items: [ComponentRef]
}

type GridProps {
  items: [ComponentRef]
}

type Grid {
  config: GridConfig
  props: GridProps
}

produced a single merged GridConfigClass used for both config and props, instead of two separate GridConfig/GridProps interfaces. --no-combine-classes had no effect, since that flag only applies to JSON input.

Root cause

quicktype's class type identity/deduplication (classTypeIdentity in TypeBuilder.getClassType) and the CombineClasses graph-rewrite pass both compared classes purely by structural shape (their properties). For JSON/JSON Schema input this is desired — structurally-identical inferred classes should unify. But GraphQL input already knows which types are distinct (from the schema itself), and separate GraphQL types should never be unified just because they happen to have the same fields.

Fix

  • packages/quicktype-graphql-input/src/index.ts: each class built from a GraphQL object type is now tagged with a new identity TypeAttributeKind (graphqlTypeName) carrying the GraphQL schema type's name.
  • packages/quicktype-core/src/Type/Type.ts: added haveSameIdentityAttributes(a, b) helper. Class type identity in TypeBuilder.getClassType already includes identity attributes, so classes with different GraphQL type names are never unified there.
  • packages/quicktype-core/src/rewrites/CombineClasses.ts: the CombineClasses rewrite pass now also requires haveSameIdentityAttributes before considering two class prototypes combinable, so structurally-identical-but-distinct GraphQL classes are not merged there either. Repeated uses of the same GraphQL type still dedupe as before, since they carry the same identity attribute.

This only affects GraphQL input; JSON/JSON Schema-derived classes carry no such identity attribute, so their existing structural deduplication/combining behavior is unchanged.

Test coverage

  • New GraphQL fixture: test/inputs/graphql/separate-types1.graphql + separate-types.gqlschema + separate-types1.1.json, modeled on the issue's Grid/GridConfig/GridProps example, wired into the existing graphql fixture mechanism (test/fixtures.ts).
  • New unit test test/unit/graphql-separate-types.test.ts: renders TypeScript from that GraphQL input with combineClasses both true and false, and asserts the output keeps Config and Props as two separate interfaces, while a genuinely-repeated type (Item) still dedupes to one interface.

Verification

  • npm run build — passes.
  • npm run test:unit — 172/172 tests pass.
  • QUICKTEST=true FIXTURE=graphql-typescript script/test — 10/10 tests pass (including the new separate-types1.graphql fixture).
  • Manually re-ran the issue's exact repro command against the built CLI: GridConfig/GridProps are now emitted as separate interfaces (both with and without --no-combine-classes), while the shared ComponentRef type used by both remains a single interface.
  • Full GraphQL fixture suite (all languages) was not run locally since several target-language toolchains aren't installed in this environment; CI will validate those.

Fixes #1072

🤖 Generated with Claude Code

schani and others added 2 commits July 20, 2026 18:30
…te (#1072)

Distinct GraphQL object types (e.g. GridConfig and GridProps) that happen
to share the same fields were unified into a single output class, since
quicktype's class deduplication only compared structural shape. This is
correct for JSON/JSON Schema input, but GraphQL input should always keep
schema-distinct types distinct since they are known-unrelated by the
schema itself, regardless of --combine-classes (which only governs
combining structurally-similar-but-not-identical JSON-derived classes).

Tag each class built from GraphQL input with an identity type attribute
carrying its GraphQL schema type name, and make both class type identity
(TypeBuilder.getClassType) and the CombineClasses rewrite respect it, so
two GraphQL classes are only unified when they come from the same named
GraphQL type. Repeated uses of the same GraphQL type still dedupe as
before.

Added a GraphQL fixture (query + introspection schema) with two
structurally-identical sibling types, and a focused unit test asserting
the generated TypeScript keeps them as separate interfaces with both
combineClasses on and off.

Co-Authored-By: gpt-5.6-sol via pi <noreply@openai.com>
@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown

Generated-output differences

111 files differ — 43 modified, 62 new, 6 deleted
8423 changed lines — +7830 / −593

Open the generated-output report →

@github-actions

Copy link
Copy Markdown

Generated-output differences

111 files differ — 43 modified, 62 new, 6 deleted
8423 changed lines — +7830 / −593

Open the generated-output report →

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Keep separate GraphQL types as separate classes

1 participant