diff --git a/.changeset/chilled-donkeys-report.md b/.changeset/chilled-donkeys-report.md new file mode 100644 index 00000000..26cb7d1e --- /dev/null +++ b/.changeset/chilled-donkeys-report.md @@ -0,0 +1,5 @@ +--- +'@hono/zod-openapi': patch +--- + +fix: Strict type checking of return values diff --git a/packages/zod-openapi/package.json b/packages/zod-openapi/package.json index 5d25c90f..1caa588d 100644 --- a/packages/zod-openapi/package.json +++ b/packages/zod-openapi/package.json @@ -38,12 +38,12 @@ }, "homepage": "https://github.com/honojs/middleware", "peerDependencies": { - "hono": ">=3.9.0", + "hono": ">=3.11.1", "zod": "3.*" }, "devDependencies": { "@hono/zod-validator": "^0.1.11", - "hono": "^3.9.1", + "hono": "^3.11.1", "zod": "^3.22.1" }, "dependencies": { @@ -53,4 +53,4 @@ "engines": { "node": ">=16.0.0" } -} +} \ No newline at end of file diff --git a/packages/zod-openapi/src/index.ts b/packages/zod-openapi/src/index.ts index 204db79e..cbbaa38a 100644 --- a/packages/zod-openapi/src/index.ts +++ b/packages/zod-openapi/src/index.ts @@ -4,6 +4,7 @@ import type { ResponseConfig, RouteConfig, ZodContentObject, + ZodMediaTypeObject, ZodRequestBody, } from '@asteasolutions/zod-to-openapi' import { @@ -150,11 +151,12 @@ type ConvertPathType = T extends `${infer Start}/{${infer Para ? `${Start}/:${Param}${ConvertPathType}` : T -type HandlerResponse = - | TypedResponse - | Promise> +type HandlerTypedResponse = TypedResponse | Promise> +type HandlerAllResponse = | Response | Promise + | TypedResponse + | Promise> export type OpenAPIHonoOptions = { defaultHook?: Hook @@ -171,7 +173,23 @@ export type RouteHandler< InputTypeForm & InputTypeJson, P extends string = ConvertPathType -> = Handler>> +> = Handler< + E, + P, + I, + // If response type is defined, only TypedResponse is allowed. + R extends { + responses: { + [statusCode: string]: { + content: { + [mediaType: string]: ZodMediaTypeObject + } + } + } + } + ? HandlerTypedResponse> + : HandlerAllResponse> +> export type RouteHook< R extends RouteConfig, @@ -186,8 +204,8 @@ export type RouteHook< > = Hook> export type OpenAPIObjectConfigure = - OpenAPIObjectConfig | - ((context: Context) => OpenAPIObjectConfig) + | OpenAPIObjectConfig + | ((context: Context) => OpenAPIObjectConfig) export class OpenAPIHono< E extends Env = Env, @@ -214,7 +232,23 @@ export class OpenAPIHono< P extends string = ConvertPathType >( route: R, - handler: Handler>>, + handler: Handler< + E, + P, + I, + // If response type is defined, only TypedResponse is allowed. + R extends { + responses: { + [statusCode: string]: { + content: { + [mediaType: string]: ZodMediaTypeObject + } + } + } + } + ? HandlerTypedResponse> + : HandlerAllResponse> + >, hook: Hook> | undefined = this.defaultHook ): OpenAPIHono>, BasePath> => { this.openAPIRegistry.registerPath(route) diff --git a/packages/zod-openapi/test/index.test.ts b/packages/zod-openapi/test/index.test.ts index b1592986..9817df97 100644 --- a/packages/zod-openapi/test/index.test.ts +++ b/packages/zod-openapi/test/index.test.ts @@ -622,6 +622,14 @@ describe('Types', () => { > expectTypeOf(appRoutes).toMatchTypeOf }) + + // @ts-expect-error it should throw an error if the types are wrong + app.openapi(route, (c) => { + return c.jsonT({ + id: '123', // should be number + message: 'Success', + }) + }) }) describe('Routers', () => { @@ -1140,7 +1148,7 @@ describe('Context can be accessible in the doc route', () => { } ) - app.doc('/doc', context => ({ + app.doc('/doc', (context) => ({ openapi: '3.0.0', info: { version: '1.0.0', @@ -1149,7 +1157,7 @@ describe('Context can be accessible in the doc route', () => { })) it('Should return with the title set as specified in env', async () => { - const res = await app.request('/doc', null, { TITLE: 'My API' }) + const res = await app.request('/doc', {}, { TITLE: 'My API' }) expect(res.status).toBe(200) expect(await res.json()).toEqual({ openapi: '3.0.0', diff --git a/yarn.lock b/yarn.lock index 1dc603b5..a1c17075 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6935,11 +6935,16 @@ heap-js@^2.2.0: resolved "https://registry.yarnpkg.com/heap-js/-/heap-js-2.3.0.tgz#8eed2cede31ec312aa696eef1d4df0565841f183" integrity sha512-E5303mzwQ+4j/n2J0rDvEPBN7GKjhis10oHiYOgjxsmxYgqG++hz9NyLLOXttzH8as/DyiBHYpUrJTZWYaMo8Q== -hono@^3.0.0, hono@^3.1.0, hono@^3.1.2, hono@^3.1.5, hono@^3.5.1, hono@^3.5.2, hono@^3.5.8, hono@^3.7.2, hono@^3.7.3, hono@^3.9.1, hono@^3.9.2: +hono@^3.0.0, hono@^3.1.0, hono@^3.1.2, hono@^3.1.5, hono@^3.5.1, hono@^3.5.2, hono@^3.5.8, hono@^3.7.2, hono@^3.7.3, hono@^3.9.2: version "3.9.2" resolved "https://registry.yarnpkg.com/hono/-/hono-3.9.2.tgz#db31a6ce733131ee16bce0c9bd031a0708ebe052" integrity sha512-180NOiMadqU3lGmN6ajPDZvZPWus3a9mtVaAUR9uG0SImngBwRLA8vbnV0oUfUAgFT4nX55sGV9dVA06OuikHA== +hono@^3.11.1: + version "3.11.1" + resolved "https://registry.yarnpkg.com/hono/-/hono-3.11.1.tgz#2f5893ae02baf55e1c5ad1e5b3a75936e3942c88" + integrity sha512-8FNUh8p/dkw8qYFxy8IJA500iW9NSeuvuwRfLw0FGkE/blCxtqWqxWTB+NaLSK3qGpaudzKur6s40Q0kpO0E+w== + hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"