使用Hono与Zod创建通用路由时遇TypeScript类型错误
解决Hono抽离路由处理函数后的TypeScript类型错误
这个错误不是bug,是你抽离函数时没给Context指定正确的类型,导致TypeScript无法推断出c.json()的合法参数类型,才抛出"never"类型的错误。
错误场景还原
你大概率写了类似这样的代码:
import { Hono } from 'hono' import { z } from 'zod' import { zValidator } from '@hono/zod-validator' const app = new Hono() // 抽离的处理函数,没指定Context类型 const handleGetUser = (c) => { const { id } = c.req.valid('param') // 这里会报错:Argument of type '"json"' is not assignable to parameter of type 'never' return c.json({ id, name: 'Shahriar' }) } app.get('/users/:id', zValidator('param', z.object({ id: z.string() })), handleGetUser)
解决方法
给抽离的处理函数的参数c指定包含校验后参数类型的Hono Context即可,有两种常用写法:
写法一:手动定义Context类型
import { Hono, Context } from 'hono' import { z } from 'zod' import { zValidator } from '@hono/zod-validator' const app = new Hono() // 先定义参数校验的Schema const UserParamSchema = z.object({ id: z.string() }) // 定义包含参数类型的Context type UserContext = Context<{ Param: z.infer<typeof UserParamSchema> }> // 给处理函数指定Context类型 const handleGetUser = (c: UserContext) => { const { id } = c.req.valid('param') return c.json({ id, name: 'Shahriar' }) } app.get('/users/:id', zValidator('param', UserParamSchema), handleGetUser)
写法二:使用Hono自带的RouteHandler类型
import { Hono, RouteHandler } from 'hono' import { z } from 'zod' import { zValidator } from '@hono/zod-validator' const app = new Hono() const UserParamSchema = z.object({ id: z.string() }) // 用RouteHandler指定参数类型 const handleGetUser: RouteHandler<{ Param: z.infer<typeof UserParamSchema> }> = (c) => { const { id } = c.req.valid('param') return c.json({ id, name: 'Shahriar' }) } app.get('/users/:id', zValidator('param', UserParamSchema), handleGetUser)
原因解释
内联处理函数时,Hono的TypeScript类型系统会自动从路由的中间件(比如zValidator)推断出Context的完整类型,包括req.valid()返回的参数类型和json()方法的合法参数类型。但抽离成独立函数后,TypeScript无法自动关联路由的类型信息,只能把c推断成最宽泛的未知类型,导致方法的类型丢失,最终出现"never"类型错误。
通用路由生成函数的优化
如果要做通用的路由生成工具,可以用泛型封装,让类型自动推断:
import { Hono, Context } from 'hono' import { z, ZodSchema } from 'zod' import { zValidator } from '@hono/zod-validator' const app = new Hono() // 通用路由生成函数 function createGetRoute<ParamSchema extends ZodSchema>( path: string, paramSchema: ParamSchema, handler: (c: Context<{ Param: z.infer<ParamSchema> }>) => ReturnType<Context['json']> ) { app.get(path, zValidator('param', paramSchema), handler) } // 使用示例 createGetRoute('/users/:id', z.object({ id: z.string() }), (c) => { const { id } = c.req.valid('param') return c.json({ id, name: 'Shahriar' }) })
内容的提问来源于stack exchange,提问作者Shahriar
相关产品推荐
相关产品推荐

