GraphQL 输入类型可以继承自其他类型或接口吗?

Can a GraphQL input type inherit from another type or interface?

是否可以对 GraphQL 输入类型使用继承?

类似的东西(当然,这不适用于输入类型):

interface UserInputInterface {
  firstName: String
  lastName: String
}

input UserInput implements UserInputInterface {
  password: String!
}

input UserChangesInput implements UserInputInterface {
  id: ID!
  password: String
}

不,规范不允许输入类型实现接口。并且 GraphQL 类型系统通常不定义任何形式的继承(extends 关键字将字段添加到现有类型,而不是用于继承)。该规范有意限制为保持简单。这意味着您无法跨输入类型重复字段。

就是说,根据您构建模式的方式,您可以构建某种类型的转换器,它基于某些元数据以编程方式附加公共字段,例如指令。

更好的是,您可以通过组合来解决您的问题(请始终牢记 composition over inheritance)。 例如

input Name {
  firstName: String
  lastName: String
}

input UserInput {
  name: Name
  password: String!
}

input UserChangesInput {
  name: Name
  id: ID!
  password: String
}

客户端现在必须将对象发送到更深的层次,但这听起来不像是避免大的重复块的代价。这实际上可能对客户也有好处,因为他们现在可以拥有构建名称的通用逻辑,而不管 query/mutation 使用它们。

在这个例子中,它只有 2 个简单的字段,这种方法有点矫枉过正,但总的来说 - 我认为这是可行的方法。

June2018 stable version of the GraphQL spec 开始,输入对象类型可以扩展 另一个输入对象类型:

Input object type extensions are used to represent an input object type which has been extended from some original input object type.

这本身不是继承;您只能扩展基本类型,不能基于它创建新类型:

extend input MyInput {
  NewField: String
}

请注意,新类型没有名称;现有 MyInput 类型已扩展。

JavaScript 参考实现 implemented Input Object extensions in GraphQL.js v14 (June 2018), though it's unclear how to actually pass the extended input fields to a query 没有出现错误。

有关实际类型继承,请参阅 graphql-s2s library

如果您是来这里寻找“implements”关键字的解释,这里是:

An object type must be a super‐set of all interfaces it implements. The object type must include a field of the same name for every field defined in an interface.

(摘自 June 2018 GraphQL spec。)

举个例子


interface Foo {
  id: ID!
  foo: Int!
}

type Bar implements Foo @entity {
  id: ID!;
  foo: Int!;
  bar: Int!;
}

因此 Bar 类型不 继承 来自 Foo 接口,但它 实现 它.前者必须包括后者列出的所有字段。

我认为这是一种注释类型的好方法,这些类型应该与其他类型相同。

使用自定义指令是可行的。

代码摘要

const typeDefs = gql`
  directive @inherits(type: String!) on OBJECT

  type Car {
    manufacturer: String
    color: String
  }
  
  type Tesla @inherits(type: "Car") {
    manufacturer: String
    papa: String
    model: String
  }
  
  type Query {
    tesla: Tesla
  }
`;

const resolvers = {
    Query: {
        tesla: () => ({ model: 'S' }),
    },
    Car: {
        manufacturer: () => 'Ford',
        color: () => 'Orange',
    },
    Tesla: {
        manufacturer: () => 'Tesla, Inc',
        papa: () => 'Elon',
    },
};

class InheritsDirective extends SchemaDirectiveVisitor {
    visitObject(type) {
        const fields = type.getFields();
        const baseType = this.schema.getTypeMap()[this.args.type];
        Object.entries(baseType.getFields()).forEach(([name, field]) => {
            if (fields[name] === undefined) {
                fields[name] = { ...field };
            }
        });
    }
}

const schemaDirectives = {
    inherits: InheritsDirective,
};

查询:

query {
  tesla {
    manufacturer
    papa
    color
    model
  }
}

输出:

{
  "data": {
    "tesla": {
      "manufacturer": "Tesla, Inc",
      "papa": "Elon",
      "color": "Orange",
      "model": "S",
    }
  }
}

https://github.com/jeanbmar/graphql-inherits 的工作示例。