Hono + zod-openapi でレスポンスに余計なfieldを含めても型エラーにならない問題
投稿日: 2025/08/31
更新日: 2025/09/02
結論
Issueで提案されてる実行時検証が良い
余計なfieldを含んでいても型エラーにならない問題
Typescript と Hono と openapi-zod を使っているときの話です。
余計なfieldを含んでいても型エラーにならない問題がります。余計なkeyと言ってもいいかも。
以下のissueで報告されています。
例えばopenapi-zodで以下のschemaを定義しているとします。idだけを持つレスポンスを返すとします。
const UserResponseSchema = z .object({ id: z.string(), }) .openapi("UserResponseSchema")
これを使ったAPIのエンドポイントを追加するとき、余計なfieldを含めてみましょう。今回はpasswordを追加しています。Hono openapi-zodのレスポンスの型はゆるくて型エラーになりません。
app.openapi( createRoute({ method: "get", path: "/users/1", responses: { 200: { content: { "application/json": { schema: UserResponseSchema, }, }, description: "return a user", }, }, }), async (c) => { const responseUser = { id: "1", password: "xxxx", } return c.json(responseUser, 200) }, )
これでは間違ったfieldが含まれたときに気付けなくて困ります。
せっかくTypescriptを使っているので型エラーで検知したいです。
ちなみにschemaに定義しているfieldが足りないときは型エラーになります。
issueで提案されてる方法
zodでパースして実行時に余計なfieldが含まれているときエラーにするコードが提案されています。
import type { Context } from "hono"; import type { ContentfulStatusCode } from "hono/utils/http-status"; import type { z, ZodSchema } from "zod"; export function strictJSONResponse< C extends Context, S extends ZodSchema, D extends Parameters<Context["json"]>[0] & z.infer<S>, U extends ContentfulStatusCode >(c: C, schema: S, data: D, statusCode?: U) { const validatedResponse = schema.safeParse(data); if (!validatedResponse.success) { return c.json( { message: "Strict response validation failed", }, 500 ); } return c.json(validatedResponse.data, statusCode); }
openapi-zodのschemaは.strict()
してる必要があります。
使い方はこんな感じかな
return strictJSONResponse( c, z .object({ id: z.string(), }) .strict(), { id: "1" }, 200, )
試したけどだめだった方法
レスポンスのschemaから型を作成し、オブジェクトを検証する。
以下のようなイメージです。
import { createRoute, OpenAPIHono } from "@hono/zod-openapi" import { z } from "@hono/zod-openapi" const app= new OpenAPIHono() const UserResponseSchema = z .object({ id: z.string(), }) .openapi("UserResponseSchema") app.openapi( createRoute({ method: "get", path: "/users/1", responses: { 200: { content: { "application/json": { schema: UserResponseSchema, }, }, description: "return a user", }, }, }), async (c) => { const responseUser: z.infer<typeof UserResponseSchema> = { id: "1", password: "xxxx", } return c.json(responseUser, 200) }, )
z.infer<typeof UserResponseSchema>
という型で検証するようにしました。
これによってpassword
の部分で型エラーが出ます。
error TS2353: Object literal may only specify known properties, and 'password' does not exist in type '{ id: string; }'. 80 password: "xxxx", ~~~~~~~~
しかし、Typescriptは子要素のfieldで型エラーが出ないケースが存在し、実用的でありませんでした。
const a = { a: 0, b: { a: 0, b: 1 } } const c = (d: { a: number; b: { a: number } }) => {} c(a) // 型エラーにならない c({ a: 0, b: { a: 0, b: 1 } }) // 型エラーになる
詳しくはこの辺の記事に書かれている
入れ子になったfieldsやmapメソッドや関数を使って代入したときに型エラーが出なくなってしまう。

Noh を作ってるエンジニア。