-
|
Hey everyone 👋 I’ve been exploring Hono’s It feels like there might be a more “dynamic” approach by inferring the Env from the middleware chain instead. For example, something along these lines: Create the app via Attach middleware using Then infer the final Env type from the resulting app instance using something like: type AppInstance = ReturnType<typeof factory>;
type Env = AppInstance extends Hono<infer E, any, any> ? E : never;
// Especially since middleware can already declare their own Env extensions, e.g.:
const middleware = () =>
createMiddleware<{
Variables: {
user: User;
};
}>(async (c, next) => {
const session = await auth.api.getSession({ headers: c.req.raw.headers });
if (!session) throw new APIError("UNAUTHORIZED");
c.set("user", session.user);
await next();
});So in theory, the Env could be “built up” from all attached middleware rather than declared upfront. My questions are: Is there a technical limitation in TypeScript that prevents Env from being inferred this way across chained .use() calls? Is this primarily a design decision in Hono for explicitness and safety? Has anyone experimented with a type-safe “Env accumulation” pattern or wrapper around Hono that achieves this? Would love to understand the tradeoffs here 🙏 |
Beta Was this translation helpful? Give feedback.
Replies: 3 comments 2 replies
-
|
Good question — this is fundamentally a TypeScript limitation, not just a Hono design choice. Why TypeScript can't infer Env from
|
Beta Was this translation helpful? Give feedback.
-
|
Hello @Andrew1326 , thanks for your response. // What you'd want:
const app = new Hono()
.use(authMiddleware) // adds { Variables: { user: User } }
.use(dbMiddleware) // adds { Variables: { db: Database } }
.get('/', (c) => {
c.get('user') // should know about User
c.get('db') // should know about Database
})the context knows about autth and db |
Beta Was this translation helpful? Give feedback.
-
The Trade-offs of "Env Accumulation" in HonoThis is a fascinating architectural question! You've correctly identified that Hono's The Technical Limitation (The "Circular Type" Problem): If we tried to infer Why the Factory Pattern is Preferred:
How to achieve "Better" Inference:If you want to avoid redeclaring types, the best approach is to define your type MyVariables = { user: User } & { logger: Logger };
type MyEnv = { Variables: MyVariables };
const factory = createFactory<MyEnv>();Is it possible?Theoretically, yes, via a Builder Pattern wrapper, but it adds a lot of "Type Gymnastics" that might make the code harder for others to read. Hono prioritizes a balance of "zero-dependency" and "straightforward types." If this helps clarify the design decisions behind Hono's Env system, please mark it as the answer! |
Beta Was this translation helpful? Give feedback.
The Trade-offs of "Env Accumulation" in Hono
This is a fascinating architectural question! You've correctly identified that Hono's
createFactory<Env>pattern is "explicit-first," and you're wondering if we can pivot to an "inference-first" model.The Technical Limitation (The "Circular Type" Problem):
In TypeScript, once you instantiate
const app = new Hono(), its genericEnvis "locked" to either what you provided or the defaultBlankEnv. While.use()can extend the internal state of the application, it cannot "reach back" and mutate the type of theappinstance itself without creating a deep chain ofReturnTypenesting.If we tried to infer
Envfrom the.use()chain, every call to.use()…