MichalLytek / MichalLytek/type-graphql

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

Open
#1,810 1 comment 2 reactions 2 assignees Claimed by @MichalLytek View on GitHub
Bug :bug: Community :family_man_girl:
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

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.