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.
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, causingCannotDetermineGraphQLTypeError.Root Cause
In
src/metadata/metadata-storage.ts, thebuildClassMetadatamethod assigns the same array reference fromfieldsCacheto both the objectType and inputType definitions:The
build()method calls:For a dual-type class, both definitions share the same
def.target, sothis.fieldsCache.get(def.target)returns the same array. Both definitions end up pointing to the same array instance.Later,
buildFieldResolverMetadatapushes field resolver entries intotypeMetadata.fields:Since both objectType and inputType share the same array, the pushed field resolver entry appears in both — causing
CannotDetermineGraphQLTypeErrorwhen 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
Suggested Fix
Clone the array on assignment in
buildClassMetadata:This ensures each definition (objectType and inputType) gets its own copy, so mutations to one don't affect the other.
Versions
Workaround
We're using a Yarn patch that applies the one-line fix above to both CJS and ESM builds.