JavaScript 中 GraphQL 解析器的自动补全
问题
当在 GraphQL 中使用 TypeScript 时,您总能获得 GraphQL 解析器中 Prisma Client 实例的自动补全,因为 `context` 对象可以被类型化——无论人们是使用 Nexus、TypeGraphQL 还是 SDL first。这极大地有助于自动补全并防止不必要的错误。
不幸的是,当您使用纯 JavaScript 工作时,这需要多做一些努力。假设我们有一个像这样的解析器
filterPosts: (parent, args, ctx) => {
  return ctx.prisma.post.findMany({
    where: {
      OR: [
        { title: { contains: args.searchString } },
        { content: { contains: args.searchString } },
      ],
    },
  })
}
现在,每当您输入 `ctx.` 时,VS Code 都会在自动补全中提供不必要的选项,这是不希望看到的。

VS Code 不知道 `context` 对象的*类型*,因此它无法为其提供任何智能提示,这就是为什么会显示不必要的建议。
解决方案
为了克服这个问题,您需要添加一个名为 `typedef` 的 JSDoc 注释,以“导入” `PrismaClient` 实例的正确类型。
// Add this to the top of the file
/**
 * @typedef { import("@prisma/client").PrismaClient } Prisma
 */
注意:您可以在这里了解更多关于 JSDoc 的信息。
最后,您需要为解析器参数添加类型。为简单起见,请忽略 `parent` 和 `args` 参数。因此,解析器现在应该看起来像这样
/**
 * @param {any} parent
 * @param {{ searchString: string }} args
 * @param {{ prisma: Prisma }} ctx
 */
filterPosts: (parent, args, ctx) => {
  return ctx.prisma.post.findMany({
    where: {
      OR: [
        { title: { contains: args.searchString } },
        { content: { contains: args.searchString } },
      ],
    },
  })
}
这将告诉 VS Code,`context` 有一个名为 `prisma` 的属性,其类型是上面 `@typedef` 中定义的 `Prisma`。
瞧,纯 JavaScript 中的自动补全实现了。

最终文件应如下所示
/**
 * @typedef { import("@prisma/client").PrismaClient } Prisma
 * @typedef { import("@prisma/client").UserCreateArgs } UserCreateArgs
 */
const { makeExecutableSchema } = require('graphql-tools')
const typeDefs = `
type User {
  email: String!
  id: ID!
  name: String
  posts: [Post!]!
}
type Post {
  author: User
  content: String
  id: ID!
  published: Boolean!
  title: String!
}
type Query {
  feed: [Post!]!
  filterPosts(searchString: String): [Post!]!
  post(where: PostWhereUniqueInput!): Post
}
type Mutation {
  createDraft(authorEmail: String, content: String, title: String!): Post!
  deleteOnePost(where: PostWhereUniqueInput!): Post
  publish(id: ID): Post
  signupUser(data: UserCreateInput!): User!
}
input PostWhereUniqueInput {
  id: ID
}
input UserCreateInput {
  email: String!
  id: ID
  name: String
  posts: PostCreateManyWithoutPostsInput
}
input PostCreateManyWithoutPostsInput {
  connect: [PostWhereUniqueInput!]
  create: [PostCreateWithoutAuthorInput!]
}
input PostCreateWithoutAuthorInput {
  content: String
  id: ID
  published: Boolean
  title: String!
}
`
const resolvers = {
  Query: {
    /**
     * @param {any} parent
     * @param {any} args
     * @param {{ prisma: Prisma }} ctx
     */
    feed: (parent, args, ctx) => {
      return ctx.prisma.post.findMany({
        where: { published: true },
      })
    },
    /**
     * @param {any} parent
     * @param {{ searchString: string }} args
     * @param {{ prisma: Prisma }} ctx
     */
    filterPosts: (parent, args, ctx) => {
      return ctx.prisma.post.findMany({
        where: {
          OR: [
            { title: { contains: args.searchString } },
            { content: { contains: args.searchString } },
          ],
        },
      })
    },
    /**
     * @param {any} parent
     * @param {{ where: { id: string }}} args
     * @param {{ prisma: Prisma }} ctx
     */
    post: (parent, args, ctx) => {
      return ctx.prisma.post.findUnique({
        where: { id: Number(args.where.id) },
      })
    },
  },
  Mutation: {
    /**
     * @param {any} parent
     * @param {{ title: string, content: string, authorEmail: (string|undefined) }} args
     * @param {{ prisma: Prisma }} ctx
     */
    createDraft: (parent, args, ctx) => {
      return ctx.prisma.post.create({
        data: {
          title: args.title,
          content: args.content,
          published: false,
          author: args.authorEmail && {
            connect: { email: args.authorEmail },
          },
        },
      })
    },
    /**
     * @param {any} parent
     * @param {{ where: { id: string }}} args
     * @param {{ prisma: Prisma }} ctx
     */
    deleteOnePost: (parent, args, ctx) => {
      return ctx.prisma.post.delete({
        where: { id: Number(args.where.id) },
      })
    },
    /**
     * @param {any} parent
     * @param {{ id: string }} args
     * @param {{ prisma: Prisma }} ctx
     */
    publish: (parent, args, ctx) => {
      return ctx.prisma.post.update({
        where: { id: Number(args.id) },
        data: { published: true },
      })
    },
    /**
     * @param {any} parent
     * @param {UserCreateArgs} args
     * @param {{ prisma: Prisma }} ctx
     */
    signupUser: (parent, args, ctx) => {
      return ctx.prisma.user.create(args)
    },
  },
  User: {
    /**
     * @param {{ id: number }} parent
     * @param {any} args
     * @param {{ prisma: Prisma }} ctx
     */
    posts: (parent, args, ctx) => {
      return ctx.prisma.user
        .findUnique({
          where: { id: parent.id },
        })
        .posts()
    },
  },
  Post: {
    /**
     * @param {{ id: number }} parent
     * @param {any} args
     * @param {{ prisma: Prisma }} ctx
     */
    author: (parent, args, ctx) => {
      return ctx.prisma.post
        .findUnique({
          where: { id: parent.id },
        })
        .author()
    },
  },
}
const schema = makeExecutableSchema({
  resolvers,
  typeDefs,
})
module.exports = {
  schema,
}