9. Codegen
15m

概述

我们的缺少一些类型安全。我们需要一种方法将我们的类型转换为可以传递给我们的ListingAPI类和函数的 TypeScript 类型。手动编写这些类型需要大量工作,而且很容易与我们的不同步。幸运的是,GraphQL Code Generator 可以帮助我们!

在本课程中,我们将

  • 为我们的函数和生成服务器端类型

GraphQL Codegen

GraphQL Code Generator读取并生成我们在整个服务器中都可以使用的 TypeScript 类型。它可以防止我们的 TypeScript 类型在更改架构时过时,使我们可以专注于开发架构,而不是不断更新类型定义!我们的架构应该是我们的的真实来源,GraphQL Code Generator 使我们能够将它作为真实来源。

安装依赖项

让我们先导航到项目根目录的终端,然后安装三个开发依赖项:@graphql-codegen/cli@graphql-codegen/typescript@graphql-codegen/typescript-resolvers

npm install -D @graphql-codegen/cli @graphql-codegen/typescript @graphql-codegen/typescript-resolvers

接下来,让我们创建一个可以在我们的package.json文件中运行的 codegen 命令。在scripts对象下添加一个名为generate的新条目,并将其设置为graphql-codegen

package.json
"scripts": {
"compile": "tsc",
"dev": "ts-node-dev --respawn ./src/index.ts",
"start": "npm run compile && nodemon ./dist/index.js",
"test": "jest",
"generate": "graphql-codegen"
},

要成功运行此命令,我们需要一个文件来包含 GraphQL Code Generator 可以遵循的指令。

codegen 文件

让我们在项目的根目录中创建一个名为codegen.ts的文件。

📦 intro-typescript
┣ 📂 src
┣ 📄 codegen.ts
┣ 📄 package.json
┣ 📄 README.md
┗ 📄 tsconfig.json

此文件将从@graphql-codegen/cli导入CodegenConfig,我们将使用它来设置所有 codegen 详细信息。

codegen.ts
import type { CodegenConfig } from "@graphql-codegen/cli";
const config: CodegenConfig = {};
export default config;

我们可以在config对象中设置的第一个属性是架构所在的目录。我们将传入相对于当前目录的文件路径。

codegen.ts
const config: CodegenConfig = {
schema: "./src/schema.graphql",
};

接下来,我们定义了生成的类型应输出的位置。在名为generates的键下,我们将添加一个包含我们所需的路径src/types.ts的对象。这将在服务器的src文件夹中创建一个名为types.ts的新文件。

codegen.ts
const config: CodegenConfig = {
schema: "./src/schema.graphql",
generates: {
"./src/types.ts": {},
},
};

最后,让我们在plugins键下告诉 Code Generator 使用哪些插件。这是一个包含typescripttypescript-resolvers的数组,用于指向我们的两个插件。

codegen.ts
generates: {
"./src/types.ts": {
plugins: ["typescript", "typescript-resolvers"],
},
},

生成类型

我们拥有运行 codegen 命令所需的一切。在根目录中打开一个终端,然后运行以下命令。

npm run generate

片刻之后,我们应该看到一个新文件已添加到服务器的src目录中:types.ts!让我们来看看。

在文件底部,我们将找到type Resolvers。此类型是通过查看架构中的对象生成的;我们目前只有两个对象,QueryListing

src/types.ts
export type Resolvers<ContextType = any> = {
Listing?: ListingResolvers<ContextType>;
Query?: QueryResolvers<ContextType>;
};

代码开始看起来有点复杂,但在幕后,TypeScript 已针对我们的架构进行了所有验证,为我们提供了可以用来表示以下内容的类型

  1. 进入每个的数据类型
  2. 这些数据上的属性,这些属性是可以访问的
  3. 每个函数返回的数据类型

因此,我们可以使用这个Resolvers类型为我们的resolvers对象带来类型安全(以及一些有用的代码验证)。

回到resolvers.ts中,我们将导入此类型并将其应用于我们的resolvers对象。

resolvers.ts
import { Resolvers } from "./types";
export const resolvers: Resolvers = {
Query: {
featuredListings: (_, __, { dataSources }) => {
return dataSources.listingAPI.getFeaturedListings();
},
},
};

这消除了我们的中的红色波浪线错误!

listing-api.ts 中添加类型注释

我们的 codegen 输出负责为架构中的生成精确的 TypeScript 类型,为我们提供了一个Listing类型,我们可以将其用于服务器代码。

types.ts
/** A particular intergalactic location available for booking */
export type Listing = {
__typename?: "Listing";
/** Indicates whether listing is closed for bookings (on hiatus) */
closedForBookings?: Maybe<Scalars["Boolean"]["output"]>;
/** The cost per night */
costPerNight?: Maybe<Scalars["Float"]["output"]>;
id: Scalars["ID"]["output"];
/** The number of beds available */
numOfBeds?: Maybe<Scalars["Int"]["output"]>;
/** The listing's title */
title: Scalars["String"]["output"];
};

让我们回到我们的文件:datasources/listing-api.ts。在文件顶部,我们将从types.ts导入Listing类型。

datasources/listing-api.ts
import { Listing } from "../types";

使用此类型,我们可以使用Promise<Listing[]>作为返回类型来注释getFeaturedListings方法。我们还将更新传递给this.get调用的类型,使其也变为Listing[]

listing-api.ts
getFeaturedListings(): Promise<Listing[]> {
return this.get<Listing[]>("featured-listings");
}

太好了!我们在listing-api.ts文件中也已准备好类型安全。

现在,你可能会问自己,我们的 函数实际上是如何访问我们刚刚创建的 的?换句话说,我们如何填充他们接收到的第三个位置参数,contextValue,使用我们想要使用的 ListingAPI 类?

好问题!答案是,我们实际上 还没有 连接我们服务器的这部分。我们将在下一课中解决这个问题!

练习

以下哪一项不包含在代码生成输出中?

关键要点

  • 使用 代码生成器,我们可以从架构 生成类型,并在代码中直接使用它们。你好,类型安全!
  • 要在我们的 上运行代码生成,我们在项目中定义一个 codegen.ts 文件。
  • codegen.ts 文件至少应指定以下属性:
    • schema,指定 的路径
    • generates,指定应输出生成类型的路径
    • plugins,列出应包含在代码生成过程中的插件
  • typescript 代码生成插件是根据 生成 TypeScript 类型的基本插件。
  • typescript-resolvers 代码生成插件将增强代码生成过程,以包括一个 Resolvers 类型,我们可以使用它来用单个、全面的类型来注释我们的

接下来

架构、?我们都有了。但它们还没有 一起 工作。如果我们运行我们的服务器,它将不知道 ListingAPI——它也不知道我们的 !让我们确保所有部分都知道彼此,并发送我们对 真实数据 的第一个查询,在下一课中。

上一页

分享您关于本课的问题和评论

此课程目前处于

测试版
.您的反馈有助于我们改进!如果您卡住了或感到困惑,请告诉我们,我们将帮助您。所有评论都是公开的,必须遵守 Apollo 行为准则。请注意,已解决或已解决的评论可能会被删除。

您需要一个 GitHub 帐户才能在下方发布。还没有? 改为在我们的 Odyssey 论坛上发帖。