diff --git a/.changeset/stale-hairs-wonder.md b/.changeset/stale-hairs-wonder.md new file mode 100644 index 00000000..ac8bca03 --- /dev/null +++ b/.changeset/stale-hairs-wonder.md @@ -0,0 +1,5 @@ +--- +'@hono/zod-openapi': patch +--- + +Fix multi-middleware complex object type inference diff --git a/packages/zod-openapi/src/index.ts b/packages/zod-openapi/src/index.ts index 74a1562f..0318ef81 100644 --- a/packages/zod-openapi/src/index.ts +++ b/packages/zod-openapi/src/index.ts @@ -221,7 +221,9 @@ type AsArray = T extends undefined // TODO move to utils? */ export type DeepSimplify = { // TODO move to utils? - [KeyType in keyof T]: T[KeyType] extends object ? DeepSimplify : T[KeyType] + [KeyType in keyof T]: T[KeyType] extends Record + ? DeepSimplify + : T[KeyType] } & {} /** diff --git a/packages/zod-openapi/test/handler.test-d.ts b/packages/zod-openapi/test/handler.test-d.ts index 384e9cf6..9e1dea14 100644 --- a/packages/zod-openapi/test/handler.test-d.ts +++ b/packages/zod-openapi/test/handler.test-d.ts @@ -1,5 +1,7 @@ import type { MiddlewareHandler } from 'hono' -import type { RouteHandler } from '../src' +import type { Equal, Expect } from 'hono/utils/types' +import type { MiddlewareToHandlerType, OfHandlerType, RouteHandler } from '../src' + import { OpenAPIHono, createRoute, z } from '../src' describe('supports async handler', () => { @@ -89,4 +91,36 @@ describe('supports async handler', () => { const hono = new OpenAPIHono() hono.openapi(routeWithMiddleware, handler) }) + + test('RouteHandler infers complex objects from multiple middleware handlers', () => { + // https://github.com/honojs/middleware/issues/847 + type CustomEnv = { Variables: { session: { id: string; createdAt: Date } } } + + const setSessionMiddleware: MiddlewareHandler = (c, next) => { + c.set('session', { id: '8e760fe8-f064-4929-b632-737f88213e57', createdAt: new Date() }) + return next() + } + + const validateSessionMiddleware: MiddlewareHandler = async (c, next) => { + const session = c.get('session') + if ((new Date().getTime() - session.createdAt.getTime()) / 1000 / 60 / 60 > 1) { + return c.json({ message: 'Unauthorized' }, 401) + } + return await next() + } + + type Example = MiddlewareToHandlerType< + [typeof setSessionMiddleware, typeof validateSessionMiddleware] + > + + // ensure the first defined env does not lose its type in multi-middleware handler + type Verify = Expect< + Equal< + OfHandlerType['env'], + { + Variables: CustomEnv['Variables'] + } + > + > + }) })