Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Type inference breaks in middleware chain with more than two middlewares #3587

Open
Ariel-Moroshko opened this issue Oct 30, 2024 · 2 comments
Labels

Comments

@Ariel-Moroshko
Copy link
Contributor

What version of Hono are you using?

4.6.8

What runtime/platform is your app running on?

Bun

What steps can reproduce the bug?

type UserContext = {
  Variables: {
    userId: string
  }
}

// Works as expected - both middlewares maintain correct type
const app1 = new Hono().get(
  "/ws",
  createMiddleware<UserContext>(async (c, next) => {
    c.set('userId', "12345")
    await next()
  }),
  async (c, next) => { // c is correctly inferred as Context<UserContext, "/ws", {}>
    await next();
  }
);

// Type inference breaks in the middle middleware
const app2 = new Hono().get(
  "/ws",
  createMiddleware<UserContext>(async (c, next) => {
    c.set('userId', "12345")
    await next()
  }),
  async (c, next) => { // c is incorrectly inferred as Context<BlankEnv, "/ws", {}>
    await next();
  },
  async (c, next) => { // c is correctly inferred as Context<UserContext, "/ws", {}>
    await next();
  }
);

What is the expected behavior?

The UserContext type should be maintained throughout the entire middleware chain, regardless of the number of middlewares.

What do you see instead?

In a chain with more than two middlewares, intermediate middlewares lose the custom context type and revert to BlankEnv, while the last middleware maintains the correct type.

Additional information

It's possible to type the Hono instance itself with new Hono<UserContext>().
While this approach fixes the type inference, it introduces a type safety issue. The variables become accessible in all middlewares, even before they are actually set. For example, if userId is only set in the second middleware, the type system would incorrectly allow access to it in the first middleware where it hasn't been initialized yet.

@yusukebe
Copy link
Member

Hi @Ariel-Moroshko

Thank you for the issue. This is a bug. Or it has not been implemented well.

Hi @m-shaka !

I think the following change will fix it. What do you think of it?

diff --git a/src/types.ts b/src/types.ts
index 101eed7f..161f8764 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -210,7 +210,7 @@ export interface HandlerInterface<
     I2 extends Input = I,
     I3 extends Input = I & I2,
     E2 extends Env = E,
-    E3 extends Env = E,
+    E3 extends Env = IntersectNonAnyTypes<[E, E2]>,
     E4 extends Env = IntersectNonAnyTypes<[E, E2, E3]>
   >(
     path: P,

@m-shaka
Copy link
Contributor

m-shaka commented Oct 31, 2024

That seems to work!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants