为什么 TypeGraphQL 不能确定泛型的类型?

Why can't TypeGraphQL determine the type of a generic?

我正在尝试为在 GraphQL 中构建连接和边缘定义通用类型定义。除了为了方便起见,我还包括了常规的 nodes 连接 属性 之外,我与 Relay 规范保持一致。 TypeGraphQL docs 展示了如何做一些非常相似的事情,但是当我尝试 运行 服务器时出现以下错误:

Cannot determine GraphQL output type for 'nodes' of 'Connection' class. Is the value, that is used as its TS type or explicit type, decorated with a proper decorator or is it a proper output value?

通常,当 class 定义中缺少 @ObjectType 装饰器时会出现这种情况,然后在其他地方将其用作字段类型;但是,我已经确认我传递的类型肯定是用这个装饰器定义的,而且我也尝试过使用我的模式中的几种不同类型。文档演示了使用泛型作为字段定义,因此这似乎也不是问题。

我的类型定义如下:

interface RawEdge<NodeType> {
  node: NodeType
}

interface Edge<NodeType> extends RawEdge<NodeType> {
  cursor: string
}

function Edge<NodeType>(NodeClass: ClassType<NodeType>) {
  @ObjectType({ isAbstract: true })
  abstract class Edge {
    constructor(identifier: string, node: NodeType) {
      this.cursor = Buffer.from(identifier).toString('base64')
      this.node = node
    }

    @Field()
    cursor: string

    @Field(type => NodeClass)
    node: NodeType
  }

  return Edge
}


interface Connection<NodeType, EdgeType extends Edge<NodeType>> {
  totalCount: number
  edges: EdgeType[]
  nodes: NodeType[]
  pageInfo: PageInfo
}

function Connection<NodeType, EdgeType extends Edge<NodeType>>(
  NodeClass: ClassType<NodeType>,
  EdgeClass: ClassType<Edge<NodeType>>
) {
  @ObjectType({ isAbstract: true })
  abstract class Connection {
    constructor(edges: EdgeType[], page: PageQuery) {
      this.totalCount = edges.length
      this.edges = edgesToReturn<EdgeType>(edges, page)
      this.nodes = this.edges.map(edge => edge.node)
      this.pageInfo = pageInfo(this, edges)
    }

    @Field(type => Int)
    totalCount: number

    @Field(type => [EdgeClass])
    edges: EdgeType[]

    @Field(type => [NodeClass])
    nodes: NodeType[]

    @Field()
    pageInfo: PageInfo
  }

  return Connection
}

编辑:以下解决方法解决了类型问题,这证明传入的类型在其定义中具有正确的装饰器。但是,这非常笨重,所以我想尽可能避免使用它。

function Edge<NodeType>(NodeClass: ClassType<NodeType>) {
  @ObjectType({ isAbstract: true })
  abstract class Edge {
    constructor(identifier: string, node: NodeType) {
      this.cursor = Buffer.from(identifier).toString('base64')
      this.node = node
    }

    @Field()
    cursor: string

    // @Field decorator removed
    node: NodeType
  }

  return Edge
}

function Connection<NodeType, EdgeType extends Edge<NodeType>>(
  NodeClass: ClassType<NodeType>,
  EdgeClass: ClassType<Edge<NodeType>>
) {
  @ObjectType({ isAbstract: true })
  abstract class Connection {
    constructor(edges: EdgeType[], page: PageQuery) {
      this.totalCount = edges.length
      this.edges = edgesToReturn<EdgeType>(edges, page)
      this.nodes = this.edges.map(edge => edge.node)
      this.pageInfo = pageInfo(this, edges)
    }

    @Field(type => Int)
    totalCount: number

    @Field(type => [EdgeClass])
    edges: EdgeType[]

    // @Field decorator removed
    nodes: NodeType[]

    @Field()
    pageInfo: PageInfo
  }

  return Connection
}

type RawDepartmentProductEdge = RawEdge<Product>

@ObjectType()
class DepartmentProductEdge extends Edge(Product) {
  // Define the field type here instead of in the generic
  @Field(type => Product)
  node: Product
}

@ObjectType()
class DepartmentProductConnection extends Connection(Product, DepartmentProductEdge) {
  // Define the field type here instead of in the generic
  @Field(type => [Product])
  nodes: Product[]
}

上下文,对于任何好奇的人:

所有这一切的目的是使用类似这样的东西生成连接...

type RawDepartmentProductEdge = RawEdge<Product>

@ObjectType()
class DepartmentProductEdge extends Edge(Product) {}

@ObjectType()
class DepartmentProductConnection extends Connection(Product, DepartmentProductEdge) {}

...然后像这样填充它们...

const products = [] // Retrieve the products here
const edges = products.map(node => new DepartmentProductEdge(node.id, node)
const connection = new DepartmentProductConnection(edges, pagination)

...所以一切都尽可能干燥,但我仍然可以根据需要向边缘添加元数据。

事实证明,上面的代码工作得很好:问题出在对我的导入进行排序。我正在使用一个“桶”文件来导出我所有的 GraphQL 类型,虽然这可以很好地将它们带入解析器或中间件,但从类型定义中的桶导入会导致奇怪的循环依赖问题。

解决方法就是在进行类型声明时直接从源文件中导入类型。因此,特别适用于上面的示例:

// File system

schema
|- connection
| |- Connection.ts
| |- DepartmentProductConnection.ts
| |- Edge.ts
| |- PageInfo.ts
|- department
| |- Department.ts
|- product
| |- Product.ts
|- index.ts
// index.ts

export * from './connection/Connection'
export * from './connection/Edge'
export * from './connection/PageInfo'
export * from './connection/DepartmentProductConnection'

export * from './department/Department'
export * from './product/Product'
// DepartmentProductConnection.ts

import { ObjectType } from 'type-graphql'

// Don't do this!
// import { Connection, Edge, Product, RawEdge } from '..'

// Instead, do this.
import { Product } from '../product/Product'
import { Connection } from './Connection'
import { Edge, RawEdge } from './Edge'

type RawDepartmentProductEdge = RawEdge<Product>

@ObjectType()
class DepartmentProductEdge extends Edge(Product) {}

@ObjectType()
class DepartmentProductConnection extends Connection(Product, DepartmentProductEdge) {}