MichalLytek / MichalLytek/type-graphql
Bug: FieldResolver metadata leaks into InputType for dual-type classes (shared fields array reference 2.0.0-rc.4)
- Dominant language
- TypeScript
- Stars
- 8.1k
- Forks
- 672
- PR merge metrics
- No merged PRs in 30d
Description
## 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:
```typescript
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:
```typescript
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`:
```typescript
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
```typescript
@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`:
```diff
- 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.
Contributor guide
Assessment
This issue has not been assessed yet.