# Schema 与类型联动技巧

URL: https://caijiao.org/fastify/typescript/schema-linking
Source: docs/fastify/typescript/schema-linking.md
Description: 介绍 Fastify 中如何利用 JSON Schema 与 TypeScript 类型进行联动，提升类型安全性与开发体验。

在 [Fastify](/fastify/) 中，利用 JSON Schema 进行数据验证是一个常见的做法，而通过 TypeScript 与 JSON Schema 进行联动，可以进一步提升类型安全性和开发体验。通过正确的 Schema 与类型定义结合，开发者可以确保请求数据和响应数据的一致性，从而减少潜在的错误。

## 1. 使用 JSON Schema 定义请求和响应结构

Fastify 提供了内置的 JSON Schema 支持，可以用于定义请求体、查询字符串、路由参数和响应数据的结构。TypeScript 可以与这些 Schema 紧密结合，从而实现类型安全。

### 例子：定义请求体的 Schema 和类型

首先，我们可以使用 JSON Schema 来定义请求体的结构，并结合 TypeScript 类型来确保类型一致性。

```typescript
import fastify from 'fastify';

const app = fastify();

// 定义请求体的 JSON Schema
const bodySchema = {
  type: 'object',
  properties: {
    name: { type: 'string' },
    age: { type: 'integer' }
  },
  required: ['name', 'age']
};

// TypeScript 类型定义
interface Greeting {
  name: string;
  age: number;
}

app.post('/greet', {
  schema: {
    body: bodySchema, // 关联 JSON Schema 到请求体
  }
}, async (request, reply) => {
  const { name, age }: Greeting = request.body; // 自动推导类型为 Greeting
  return { greeting: `Hello, ${name}, you are ${age} years old` };
});

app.listen(3000, (err, address) => {
  if (err) {
    app.log.error(err);
    process.exit(1);
  }
  app.log.info(`Server listening at ${address}`);
});
```

在这个例子中，我们首先定义了一个 JSON Schema `bodySchema` 来描述请求体的数据结构。然后，我们使用 TypeScript 的接口 `Greeting` 来定义请求体的类型。Fastify 会根据 `bodySchema` 来验证请求数据，TypeScript 会自动推导出 `request.body` 的类型为 `Greeting`，确保类型一致。

## 2. 使用 TypeScript 类型生成 JSON Schema

有时，我们可能需要反向操作：通过 TypeScript 类型生成 JSON Schema。我们可以使用第三方库如 `typescript-json-schema` 或 `fastify-typegen` 来自动生成与 TypeScript 类型关联的 JSON Schema。

### 例子：通过 TypeScript 类型生成 JSON Schema

假设我们有一个 TypeScript 类型 `Greeting`，并希望生成相应的 JSON Schema 来进行验证。

```typescript
interface Greeting {
  name: string;
  age: number;
}

// 使用第三方工具生成 JSON Schema
```

你可以使用 `typescript-json-schema` 生成 JSON Schema：

```bash
npx typescript-json-schema tsconfig.json Greeting --out greeting-schema.json
```

这将生成一个与 `Greeting` 类型相对应的 JSON Schema，之后可以将它用于 Fastify 的路由验证。

## 3. 联动技巧：提高开发效率

### 3.1 使用联合类型与 JSON Schema 进行协作

在处理复杂的数据结构时，TypeScript 的联合类型（Union Types）与 JSON Schema 配合使用可以大大提升灵活性和类型安全性。

```typescript
const bodySchema = {
  type: 'object',
  properties: {
    status: { type: 'string', enum: ['active', 'inactive'] },
    data: { type: 'object' }
  },
  required: ['status', 'data']
};

// TypeScript 联合类型
type Status = 'active' | 'inactive';

interface RequestBody {
  status: Status;
  data: any;
}
```

在上面的例子中，`status` 字段的 JSON Schema 使用了枚举类型 `enum`，这与 TypeScript 中的联合类型 `Status` 保持一致。这样，开发者可以在编写代码时同时享受到类型检查和数据验证的好处。

### 3.2 结合类型和 Schema 进行响应验证

我们不仅可以使用 JSON Schema 来验证请求数据，还可以为响应数据定义 Schema，以确保响应数据符合预期结构。

```typescript
const responseSchema = {
  type: 'object',
  properties: {
    greeting: { type: 'string' }
  },
  required: ['greeting']
};

interface GreetingResponse {
  greeting: string;
}

app.post('/greet', {
  schema: {
    body: bodySchema,   // 请求体 Schema
    response: {
      200: responseSchema  // 响应体 Schema
    }
  }
}, async (request, reply) => {
  const { name, age }: Greeting = request.body;
  const response: GreetingResponse = { greeting: `Hello, ${name}, you are ${age} years old` };
  return response;  // Fastify 会根据 responseSchema 验证返回值
});
```

在这个例子中，响应数据 `GreetingResponse` 会被 JSON Schema 验证，确保返回的数据符合预期格式。

## 4. 自动化 Schema 和类型同步

为了避免手动同步 TypeScript 类型和 JSON Schema，你可以使用一些工具来实现自动化同步。比如，`fastify-typegen` 插件可以自动生成和 TypeScript 类型关联的 JSON Schema。

### 安装 fastify-typegen

```bash
npm install fastify-typegen --save-dev
```

### 配置和使用

```typescript
import fastify from 'fastify';
import fastifyTypegen from 'fastify-typegen';

const app = fastify();

app.register(fastifyTypegen);

app.post('/greet', async (request, reply) => {
  const { name, age }: Greeting = request.body;
  return { greeting: `Hello, ${name}, you are ${age} years old` };
});
```

`fastify-typegen` 会自动生成 JSON Schema 和 TypeScript 类型之间的映射，避免了手动同步带来的麻烦。

## 5. 总结

通过结合 JSON Schema 和 TypeScript 类型，开发者可以实现更强的类型安全和自动化验证，确保请求和响应数据的结构符合预期。在 Fastify 中，类型与 Schema 的联动大大提升了开发体验，减少了开发过程中因类型不一致导致的错误。通过合理配置和使用这些技术，开发者可以构建更为稳定和健壮的应用程序。
