Skip to content

Bug: FieldResolver metadata leaks into InputType for dual-type classes (shared fields array reference 2.0.0-rc.4) #1810

@Scong

Description

@Scong

Description

When a class is decorated with both @ObjectType() and @InputType() (a "dual-type" class), field resolvers registered on the objectType side incorrectly appear in the inputType's fields, causing CannotDetermineGraphQLTypeError.

Root Cause

In src/metadata/metadata-storage.ts, the buildClassMetadata method assigns the same array reference from fieldsCache to both the objectType and inputType definitions:

private buildClassMetadata(definitions: ClassMetadata[]) {
    definitions.forEach(def => {
      if (!def.fields) {
        const fields = this.fieldsCache.get(def.target) || [];
        // ... enriches fields ...
        def.fields = fields;  // <-- shared reference
      }
    });
}

The build() method calls:

this.buildClassMetadata(this.objectTypes);
this.buildClassMetadata(this.inputTypes);

For a dual-type class, both definitions share the same def.target, so this.fieldsCache.get(def.target) returns the same array. Both definitions end up pointing to the same array instance.

Later, buildFieldResolverMetadata pushes field resolver entries into typeMetadata.fields:

typeMetadata.fields!.push(fieldMetadata);

Since both objectType and inputType share the same array, the pushed field resolver entry appears in both — causing CannotDetermineGraphQLTypeError when the schema generator processes the inputType (because the field resolver's return type, e.g. a complex object, is not valid as an input type).

Reproduction

@ObjectType()
@InputType("MyClassInput")
class MyClass {
  @Field()
  id: number;
}

@Resolver(of => MyClass)
class MyClassResolver {
  @FieldResolver(returns => SomeComplexType)
  computed(@Root() root: MyClass) {
    return /* ... */;
  }
}

await buildSchema({ resolvers: [MyClassResolver] });
// Throws: CannotDetermineGraphQLTypeError: Cannot determine GraphQL input type for 'computed' of 'MyClass' class

Suggested Fix

Clone the array on assignment in buildClassMetadata:

-                def.fields = fields;
+                def.fields = [...fields];

This ensures each definition (objectType and inputType) gets its own copy, so mutations to one don't affect the other.

Versions

  • type-graphql: 2.0.0-rc.3 and 2.0.0-rc.4 (bug present in both)
  • graphql: 16.12.0
  • Node.js: 24.x

Workaround

We're using a Yarn patch that applies the one-line fix above to both CJS and ESM builds.

Metadata

Metadata

Labels

Projects

Status

In progress

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions