Skip to main content

类型提供者

类型提供者

¥Type Providers

类型提供程序是仅 TypeScript 的功能,使 Fastify 能够直接从内联 JSON 模式静态推断类型信息。它们是在路由上指定通用参数的替代方法;并且可以大大减少为项目中定义的每个模式保留关联类型的需要。

¥Type Providers are a TypeScript only feature that enables Fastify to statically infer type information directly from inline JSON Schema. They are an alternative to specifying generic arguments on routes; and can greatly reduce the need to keep associated types for each schema defined in your project.

提供者

¥Providers

类型提供程序作为附加包提供,你需要将其安装到项目中。每个提供商在底层都使用不同的推断库;允许你选择最适合你需求的库。官方类型提供程序包遵循 @fastify/type-provider-{provider-name} 命名约定,并且还有几个社区包可用。

¥Type Providers are offered as additional packages you will need to install into your project. Each provider uses a different inference library under the hood; allowing you to select the library most appropriate for your needs. Official Type Provider packages follow a @fastify/type-provider-{provider-name} naming convention, and there are several community ones available as well.

支持以下推断包:

¥The following inference packages are supported:

另请参阅每个包的类型提供程序封装器包:

¥See also the Type Provider wrapper packages for each of the packages respectively:

Json 模式到 Ts

¥Json Schema to Ts

以下设置了一个 json-schema-to-ts 类型提供程序

¥The following sets up a json-schema-to-ts Type Provider

$ npm i @fastify/type-provider-json-schema-to-ts
import fastify from 'fastify'
import { JsonSchemaToTsProvider } from '@fastify/type-provider-json-schema-to-ts'

const server = fastify().withTypeProvider<JsonSchemaToTsProvider>()

server.get('/route', {
schema: {
querystring: {
type: 'object',
properties: {
foo: { type: 'number' },
bar: { type: 'string' },
},
required: ['foo', 'bar']
}
}
}, (request, reply) => {

// type Query = { foo: number, bar: string }
const { foo, bar } = request.query // type safe!
})

TypeBox

下面设置一个 TypeBox Type Provider

¥The following sets up a TypeBox Type Provider

$ npm i @fastify/type-provider-typebox
import fastify from 'fastify'
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
import { Type } from '@sinclair/typebox'

const server = fastify().withTypeProvider<TypeBoxTypeProvider>()

server.get('/route', {
schema: {
querystring: Type.Object({
foo: Type.Number(),
bar: Type.String()
})
}
}, (request, reply) => {

// type Query = { foo: number, bar: string }
const { foo, bar } = request.query // type safe!
})

另请参阅 类型框文档 以了解如何设置 AJV 以与 TypeBox 配合使用。

¥See also the TypeBox documentation on how to set up AJV to work with TypeBox.

Zod

请参阅 官方文档 了解 Zod 类型提供程序说明。

¥See official documentation for Zod type provider instructions.

作用域类型提供者

¥Scoped Type-Provider

提供者类型不会在全局范围内传播。在封装使用中,可以重新映射上下文以使用一个或多个提供程序(例如,typeboxjson-schema-to-ts 可以在同一应用中使用)。

¥The provider types don't propagate globally. In encapsulated usage, one can remap the context to use one or more providers (for example, typebox and json-schema-to-ts can be used in the same application).

示例:

¥Example:

import Fastify from 'fastify'
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
import { JsonSchemaToTsProvider } from '@fastify/type-provider-json-schema-to-ts'
import { Type } from '@sinclair/typebox'

const fastify = Fastify()

function pluginWithTypebox(fastify: FastifyInstance, _opts, done): void {
fastify.withTypeProvider<TypeBoxTypeProvider>()
.get('/', {
schema: {
body: Type.Object({
x: Type.String(),
y: Type.Number(),
z: Type.Boolean()
})
}
}, (req) => {
const { x, y, z } = req.body // type safe
});
done()
}

function pluginWithJsonSchema(fastify: FastifyInstance, _opts, done): void {
fastify.withTypeProvider<JsonSchemaToTsProvider>()
.get('/', {
schema: {
body: {
type: 'object',
properties: {
x: { type: 'string' },
y: { type: 'number' },
z: { type: 'boolean' }
},
}
}
}, (req) => {
const { x, y, z } = req.body // type safe
});
done()
}

fastify.register(pluginWithJsonSchema)
fastify.register(pluginWithTypebox)

还需要提到的是,由于类型不会全局传播,因此目前无法避免在处理多个范围时在路由上进行多次注册,如下所示:

¥It's also important to mention that since the types don't propagate globally, currently it is not possible to avoid multiple registrations on routes when dealing with several scopes, see below:

import Fastify from 'fastify'
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
import { Type } from '@sinclair/typebox'

const server = Fastify().withTypeProvider<TypeBoxTypeProvider>()

server.register(plugin1) // wrong
server.register(plugin2) // correct

function plugin1(fastify: FastifyInstance, _opts, done): void {
fastify.get('/', {
schema: {
body: Type.Object({
x: Type.String(),
y: Type.Number(),
z: Type.Boolean()
})
}
}, (req) => {
// it doesn't work! in a new scope needs to call `withTypeProvider` again
const { x, y, z } = req.body
});
done()
}

function plugin2(fastify: FastifyInstance, _opts, done): void {
const server = fastify.withTypeProvider<TypeBoxTypeProvider>()

server.get('/', {
schema: {
body: Type.Object({
x: Type.String(),
y: Type.Number(),
z: Type.Boolean()
})
}
}, (req) => {
// works
const { x, y, z } = req.body
});
done()
}

FastifyInstance + TypeProvider 的类型定义

¥Type Definition of FastifyInstance + TypeProvider

使用模块时,必须使用带有类型提供程序泛型的 FastifyInstance。请参阅下面的示例:

¥When working with modules one has to make use of FastifyInstance with Type Provider generics. See the example below:

// index.ts
import Fastify from 'fastify'
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'
import { registerRoutes } from './routes'

const server = Fastify().withTypeProvider<TypeBoxTypeProvider>()

registerRoutes(server)

server.listen({ port: 3000 })
// routes.ts
import { Type } from '@sinclair/typebox'
import {
FastifyInstance,
FastifyBaseLogger,
RawReplyDefaultExpression,
RawRequestDefaultExpression,
RawServerDefault
} from 'fastify'
import { TypeBoxTypeProvider } from '@fastify/type-provider-typebox'

type FastifyTypebox = FastifyInstance<
RawServerDefault,
RawRequestDefaultExpression<RawServerDefault>,
RawReplyDefaultExpression<RawServerDefault>,
FastifyBaseLogger,
TypeBoxTypeProvider
>;

export function registerRoutes(fastify: FastifyTypebox): void {
fastify.get('/', {
schema: {
body: Type.Object({
x: Type.String(),
y: Type.Number(),
z: Type.Boolean()
})
}
}, (req) => {
// works
const { x, y, z } = req.body
});
}