hono icon indicating copy to clipboard operation
hono copied to clipboard

Type inference drops routes declared before use() when using method chaining

Open resYuto opened this issue 4 months ago • 3 comments

What version of Hono are you using?

~~4.6.5~~ 4.9.5 (letest at this moment. sorry!)

What runtime/platform is your app running on? (with version if possible)

Cloudflare Workers (issue observed in local type inference only)

What steps can reproduce the bug?

When defining endpoints with method chaining, if a "use()" method is inserted, the routes declared before that "use()" are not included in the inferred type information.

import { Hono } from "hono";

const routerA = new Hono()
  .get("/", (c) => c.text("Hello from Router A!"))
  .use("/:slug", async (c, next) => {
    return next();
  })
  .post("/:slug", (c) => c.text("Hello from Router A with slug!"));

const app = new Hono().route("/a", routerA);

export type AppType = typeof app;
export default app;

What is the expected behavior?

Chained routes defined before "use()" should still be inferred in the types.

What do you see instead?

No response

Additional information

No response

resYuto avatar Sep 01 '25 09:09 resYuto

@yusukebe @resYuto I’m investigating this issue, and I found the following puzzling behavior.

import { Hono } from 'hono'
import { showRoutes } from 'hono/dev'

const routes = new Hono()
  .get('/', (c) => c.text('Hello from /'))
  .use('/noop', async (c, next) => {
    await next()
  })

showRoutes(routes)
// GET  /

/**
 * Schema is expected to have only one endpoint: GET /
 * But it has GET /noop instead.
 * This means runtime routes and type-level routes are different.
 *
 * Raw Shape:
 * ```ts
 * type AppType = Hono<{}, {
 *    "/noop": {
 *        $get: {
 *            input: {};
 *            output: "Hello from /";
 *            outputFormat: "text";
 *            status: ContentfulStatusCode;
 *        };
 *    };
 *}, "/">
 *```
 */
type AppType = typeof routes

// We can see `Hello from /` when accessing http://localhost:3000/
// Bun.serve({
//   fetch: app.fetch,
//   port: 3000,
// })

sushichan044 avatar Sep 18 '25 15:09 sushichan044

I have an idea for a solution that should work while still supporting the ability to omit path specifications in chained routes. If it looks working, I’ll open a draft PR.

sushichan044 avatar Sep 18 '25 15:09 sushichan044

Hi @sushichan044

I’m investigating this issue, and I found the following puzzling behavior.

What is the puzzling behavior? If you want to show the routes of middleware with showRoutes, please set the verbose option:

showRoutes(routes, {
  verbose: true
})

yusukebe avatar Sep 19 '25 00:09 yusukebe