H
Hono•6mo ago
tbeseda

Types issue in zod-openapi in v0.19.4, .5, & .6 (repro attached)

Maybe I'm holding it wrong, but this minimal file reproduces the type error:
import {OpenAPIHono, createRoute, z} from '@hono/zod-openapi'

interface AppBindings { Variables: { userId: string } }

const route = createRoute({
method: 'get',
path: '/widget/{widgetId}',
request: {
params: z.object({ widgetId: z.string() }),
},
responses: {
200: {
content: {
'application/json': {
schema: z.object({ widgetId: z.string() }),
},
},
description: 'Success',
},
404: {
content: {
'application/json': {
schema: z.object({
error: z.object({
code: z.string(),
message: z.string(),
}),
}),
},
},
description: 'Not Found',
},
},
})

const app = new OpenAPIHono<AppBindings>()

app.openapi(route, async (c) => { // <-- type error
const {widgetId} = c.req.valid('param')
const userId = c.get('userId')

if (widgetId === 'not-found') {
return c.json(
{
error: {
code: 'NOT_FOUND',
message: 'Not found',
},
},
404,
)
}

return c.json({widgetId}, 200)
})
import {OpenAPIHono, createRoute, z} from '@hono/zod-openapi'

interface AppBindings { Variables: { userId: string } }

const route = createRoute({
method: 'get',
path: '/widget/{widgetId}',
request: {
params: z.object({ widgetId: z.string() }),
},
responses: {
200: {
content: {
'application/json': {
schema: z.object({ widgetId: z.string() }),
},
},
description: 'Success',
},
404: {
content: {
'application/json': {
schema: z.object({
error: z.object({
code: z.string(),
message: z.string(),
}),
}),
},
},
description: 'Not Found',
},
},
})

const app = new OpenAPIHono<AppBindings>()

app.openapi(route, async (c) => { // <-- type error
const {widgetId} = c.req.valid('param')
const userId = c.get('userId')

if (widgetId === 'not-found') {
return c.json(
{
error: {
code: 'NOT_FOUND',
message: 'Not found',
},
},
404,
)
}

return c.json({widgetId}, 200)
})
12 Replies
tbeseda
tbesedaOP•6mo ago
Here's the text of the type error
Argument of type '(c: Context<AppBindings, "/widget/:widgetId", { in: { param: { widgetId?: string; }; }; out: { param: { widgetId?: string; }; }; }>) => Promise<JSONRespondReturn<{ error: { code: string; message: string; }; }, 404> | JSONRespondReturn<...>>' is not assignable to parameter of type 'Handler<AppBindings, "/widget/:widgetId", { in: { param: { widgetId?: string; }; }; out: { param: { widgetId?: string; }; }; }, Promise<never>>'.
Type 'Promise<JSONRespondReturn<{ error: { code: string; message: string; }; }, 404> | JSONRespondReturn<{ widgetId: string; }, 200>>' is not assignable to type 'Promise<never>'.
Type 'JSONRespondReturn<{ error: { code: string; message: string; }; }, 404> | JSONRespondReturn<{ widgetId: string; }, 200>' is not assignable to type 'never'.
Type 'JSONRespondReturn<{ error: { code: string; message: string; }; }, 404>' is not assignable to type 'never'.ts(2345)
Argument of type '(c: Context<AppBindings, "/widget/:widgetId", { in: { param: { widgetId?: string; }; }; out: { param: { widgetId?: string; }; }; }>) => Promise<JSONRespondReturn<{ error: { code: string; message: string; }; }, 404> | JSONRespondReturn<...>>' is not assignable to parameter of type 'Handler<AppBindings, "/widget/:widgetId", { in: { param: { widgetId?: string; }; }; out: { param: { widgetId?: string; }; }; }, Promise<never>>'.
Type 'Promise<JSONRespondReturn<{ error: { code: string; message: string; }; }, 404> | JSONRespondReturn<{ widgetId: string; }, 200>>' is not assignable to type 'Promise<never>'.
Type 'JSONRespondReturn<{ error: { code: string; message: string; }; }, 404> | JSONRespondReturn<{ widgetId: string; }, 200>' is not assignable to type 'never'.
Type 'JSONRespondReturn<{ error: { code: string; message: string; }; }, 404>' is not assignable to type 'never'.ts(2345)
tbeseda
tbesedaOP•6mo ago
perhaps there's a bug in the fix for this issue šŸ¤” https://github.com/honojs/middleware/issues/1102
GitHub
Variables are of type never when there's a middleware with @hono/...
Which middleware has the bug? @hono/zod-openapi What version of the middleware? 0.19.2 What version of Hono are you using? 4.7.5 What runtime/platform is your app running on? (with version if possi...
ambergristle
ambergristle•6mo ago
i can't repro on 0.19.4 have you tried restarting your ts server, and/or reinstalling dependencies?
tbeseda
tbesedaOP•6mo ago
$ npm ls @hono/zod-openapi

myProject@0.0.1 /Users/my-project
ā”œā”€ā”€ @hono/zod-openapi@0.19.4
$ npm ls @hono/zod-openapi

myProject@0.0.1 /Users/my-project
ā”œā”€ā”€ @hono/zod-openapi@0.19.4
$ tsc repro.ts

repro.ts:42:20 - error TS2345: Argument of type '(c: Context<AppBindings, "/widget/:widgetId", { in: { param: { widgetId?: string; }; }; out: { param: { widgetId?: string; }; }; }>) => Promise<JSONRespondReturn<{ error: { code: string; message: string; }; }, 404> | JSONRespondReturn<...>>' is not assignable to parameter of type 'Handler<AppBindings, "/widget/:widgetId", { in: { param: { widgetId?: string; }; }; out: { param: { widgetId?: string; }; }; }, Promise<never>>'.
Type 'Promise<JSONRespondReturn<{ error: { code: string; message: string; }; }, 404> | JSONRespondReturn<{ widgetId: string; }, 200>>' is not assignable to type 'Promise<never>'.
Type 'JSONRespondReturn<{ error: { code: string; message: string; }; }, 404> | JSONRespondReturn<{ widgetId: string; }, 200>' is not assignable to type 'never'.
Type 'JSONRespondReturn<{ error: { code: string; message: string; }; }, 404>' is not assignable to type 'never'.

42 app.openapi(route, async (c) => {
~~~~~~~~~~~~~~
$ tsc repro.ts

repro.ts:42:20 - error TS2345: Argument of type '(c: Context<AppBindings, "/widget/:widgetId", { in: { param: { widgetId?: string; }; }; out: { param: { widgetId?: string; }; }; }>) => Promise<JSONRespondReturn<{ error: { code: string; message: string; }; }, 404> | JSONRespondReturn<...>>' is not assignable to parameter of type 'Handler<AppBindings, "/widget/:widgetId", { in: { param: { widgetId?: string; }; }; out: { param: { widgetId?: string; }; }; }, Promise<never>>'.
Type 'Promise<JSONRespondReturn<{ error: { code: string; message: string; }; }, 404> | JSONRespondReturn<{ widgetId: string; }, 200>>' is not assignable to type 'Promise<never>'.
Type 'JSONRespondReturn<{ error: { code: string; message: string; }; }, 404> | JSONRespondReturn<{ widgetId: string; }, 200>' is not assignable to type 'never'.
Type 'JSONRespondReturn<{ error: { code: string; message: string; }; }, 404>' is not assignable to type 'never'.

42 app.openapi(route, async (c) => {
~~~~~~~~~~~~~~
and with 0.19.2 it builds. (0.19.3 has the yarn workspace bug mentioned here - fixed in 0.19.4) (bump) I still see this issue in 0.19.5 Only way I see to fix it is using
as unknown as Promise<never>
as unknown as Promise<never>
😬
tbeseda
tbesedaOP•6mo ago
got this down to a minimal reproduction sandbox on Stackblitz Run npm run typecheck to see the error (the editor doesn't immediately show the issue until the file is edited)
Taylor Beseda
StackBlitz
hono-zod-type-error - StackBlitz
A TypeScript project based on @hono/zod-openapi, @types/node and typescript
ambergristle
ambergristle•6mo ago
Dope. Thanks for putting this together!
tbeseda
tbesedaOP•3mo ago
Still an issue in v0.19.6 -- I updated the reproduction above ā˜ļø and made it even simpler. npm test runs a simple typecheck tsc --noEmit --skipLibCheck main.ts and it complains with the same error that my response shapes are
not assignable to type 'Promise<never>
not assignable to type 'Promise<never>
Maybe if someone can make that pass with a change to the tsc flags/settings? It seems strange that others wouldn't have this issue, so I must have too simple of a TS setup. maybe? This is actually an issue with the example in the hono/zod-openapi/README.md too. I added the example to my test env. you can run npm t to see both files error in the same way. (The Stackblitz built-in typechecker doesn't always catch it, but if you edit the file (add a new line) it fires up.) https://stackblitz.com/edit/hono-zod-type-error-7phbanny?file=zod-openapi_README.ts&view=editor I must be doing something really dumb here. It's stripped back to the most minimal setup but I'm still getting a type error for the most important type: the handler response matches the route definition. Any help appreciated.
ambergristle
ambergristle•3mo ago
tbeseda
tbesedaOP•3mo ago
welp that did it! I added --strict to my reproduction and the types pass. thanks for the tip! wonder why that is
ambergristle
ambergristle•3mo ago
no idea. looks like some pretty significant updates were made to the typing though i'd open an issue, if you haven't already
tbeseda
tbesedaOP•3mo ago
Cool yeah I can do that. I hadn't yet because i figured it was something dumb I was doing, but it does seem like it should work (or error differently) without strict
ambergristle
ambergristle•3mo ago
it did until ~0.19.3, so worth calling out at least

Did you find this page helpful?