如何为Fastify控制器函数保留完整的类型检查?
问题:Fastify分离控制器后丢失FastifyReply类型检查
我有如下简单的Fastify服务器代码:
import { Static, Type } from "@sinclair/typebox"; const UserSchema = Type.Object({ username: Type.String(), email: Type.String({ format: "email" }), age: Type.Number(), }); const UserResponseSchema = Type.Object({ name: Type.String(), }); type UserResponseSchemaType = Static<typeof UserResponseSchema>; type UserSchemaType = Static<typeof UserSchema>; // --------------------- interface createUserRouteInterFace { Body: UserSchemaType; Reply: UserResponseSchemaType; } async function indexRoutes(fastify: FastifyInstance) { fastify.post<createUserRouteInterFace>( "/create-user", { schema: { body: UserSchema, response: { 200: UserResponseSchema } } }, createUserController // 问题所在:分离控制器后丢失Reply类型检查 ); }
如果使用内联匿名函数替代createUserController,能自动获得完整的类型检查,但大型项目中会导致代码混乱。因此我将控制器放在单独文件中,示例如下:
// controllers.ts import { FastifyRequest, FastifyReply } from "fastify"; import { UserSchemaType } from "../somewhere"; export async function createUserController( req: FastifyRequest<{ Body: UserSchemaType }>, reply: FastifyReply ) { const { username } = req.body; return reply.send({ username }); }
这种方案能正常运行,但代价是丢失了FastifyReply的类型检查。请问有什么方法可以编写控制器函数且不丢失类型检查?
解决方案
方法1:给FastifyReply指定返回类型泛型
直接在控制器的FastifyReply参数上指定对应的返回类型,让TypeScript对reply.send()的内容做类型校验:
// controllers.ts import { FastifyRequest, FastifyReply } from "fastify"; import { UserSchemaType, UserResponseSchemaType } from "../somewhere"; export async function createUserController( req: FastifyRequest<{ Body: UserSchemaType }>, reply: FastifyReply<UserResponseSchemaType> // 指定返回类型泛型 ) { const { username } = req.body; // 若返回{ username }会触发TS错误,因为响应要求的是{ name: string } return reply.send({ name: username }); }
方法2:复用路由接口类型
将路由的类型接口导出到共享文件,控制器直接复用该接口,避免重复定义类型:
首先在共享类型文件(如types.ts)中导出接口:
// types.ts import { Static, Type } from "@sinclair/typebox"; const UserSchema = Type.Object({ username: Type.String(), email: Type.String({ format: "email" }), age: Type.Number(), }); const UserResponseSchema = Type.Object({ name: Type.String(), }); export type UserResponseSchemaType = Static<typeof UserResponseSchema>; export type UserSchemaType = Static<typeof UserSchema>; export interface CreateUserRouteInterface { Body: UserSchemaType; Reply: UserResponseSchemaType; }
然后在控制器中导入并使用:
// controllers.ts import { FastifyRequest, FastifyReply } from "fastify"; import { CreateUserRouteInterface } from "../types"; export async function createUserController( req: FastifyRequest<CreateUserRouteInterface>, reply: FastifyReply<CreateUserRouteInterface["Reply"]> ) { const { username } = req.body; return reply.send({ name: username }); }
方法3:使用Fastify的RouteHandler类型
直接利用Fastify提供的RouteHandler类型,让控制器完全匹配路由处理函数的类型定义:
// controllers.ts import { RouteHandler } from "fastify"; import { CreateUserRouteInterface } from "../types"; export const createUserController: RouteHandler<CreateUserRouteInterface> = async (req, reply) => { const { username } = req.body; return reply.send({ name: username }); };
这种方式最简洁,无需手动指定req和reply的类型,TypeScript会自动从CreateUserRouteInterface中推导。
内容的提问来源于stack exchange,提问作者Iman Hosseinipour
相关产品推荐
相关产品推荐

