hono
hono copied to clipboard
Return type of `get` method on `Hono`'s subclass is `Hono`, not the subclass
What version of Hono are you using?
3.7.3
What runtime/platform is your app running on?
N/A (my issue is about typing, not a runtime problem)
What steps can reproduce the bug?
When you define a class extending Hono
and call a get
method (or other similar ones), the return value is inferred to be Hono
instead of the subclass.
Below is a reproduction code and the corresponding playground link:
import { Hono } from "hono";
class MyCustomHono extends Hono {
constructor(init?: ConstructorParameters<typeof Hono>[0]) {
super(init);
}
customMethod() {
return this;
}
}
const foo = new Hono().get("/", (c) => c.text("Hello!"));
// ^? const foo: Hono<Env, ToSchema<"get", "/", unknown, {}>, "/">;
const bar = new MyCustomHono().get("/", (c) => c.text("Hello subclass!"));
// ^? const bar: Hono<Env, ToSchema<"get", "/", unknown, {}>, "/">;
const baz = new MyCustomHono().customMethod();
// ^? const baz: MyCustomHono
What is the expected behavior?
bar
is inferred to be MyCustomHono
.
What do you see instead?
bar
is inferred to be Hono
.
Additional information
A real world example of this issue would be @hono/zod-openapi
and I actually found this behavior in that situation.
Hi @yudai-nkt
This should be fixed. I think it is because the return value of get
is specified as Hono
as follows. I can't think of a solution right now, but I will look into it.
https://github.com/honojs/hono/blob/82f103302bf07c7eb54f124768f45677e5efa41b/src/types.ts#L183
Hmmmm. Fixing this issue is super difficult or imposible.
To achieve this, we must be able to "infer the type of a subclass in the parent class Hono
and pass the types as a generic to it".
This means we must be able to this:
class Foo<Path extends string> {
route<Path extends string>(path: Path): Foo<Path> { // This can be changed as you like.
return this
}
}
const main = new Foo()
const mainRoute = main.route('/abc') // Foo<"/abc"> - OK
class SubFoo<Path extends string> extends Foo<Path> {}
const sub = new SubFoo()
const subRoute = sub.route('/abc') // Foo<"/abc"> - NG, should be SubFoo<"/abc">
- The type of
mainRoute
should beFoo<"/abc">
, notFoo<string>
. ThesubRoute
must beSubFoo<"/abc">
as well. - We don't write
new Foo<'/abc'>
. - You can change the return type of
route
inFoo
class, currentlyFoo<Path>
.
@yudai-nkt @usualoma @Code-Hex @sor4chi and others: Is there a superman who can solve this?
The zod-openapi package in the middleware repo actually has the exact same problem. I don't think there is any good way to accomplish this in typescript, though
Yeah.
Actually, before creating "Zod OpenAPI", I had not really thought about extending Hono to make a sub class. For Hono's flexible type inference, the return type of each method must be limited to Hono
maybe.
I may have forgotten to ping you, but can you solve this problem? @usualoma @Code-Hex @masnormen @NicoPlyley
@yusukebe
I believe this is not possible, but it's not a limitation of TypeScript but the intended way of doing it.
My understanding of is it that when calling a method on a subclass, the inferred return type defaults to that of the parent class. This provides as a way to indicate that methods from the parent class are also valid on the subclass.
This ensures that a subclass can be used interchangeably with its parent class without causing issues, and any overridden returns follow the types of the parent class following Liskov Substitution Principle
@NicoPlyley Thanks for explaining! I understood it well!
So, I hope we can solve real world problems.
@yudai-nkt @ZerNico Is there any specific problem with the subclass type being Hono
?
I don't think @yudai-nkt has much of a practical problem with this repository.
https://github.com/yudai-nkt/hono-zod-openapi-html#possible-contributions-to-the-ecosystem
- Cannot infer the correct type for endpoints that may return several response status
- Returning a 204 response yields a compile error
As for these two issues, you should use c.jsonT()
instead of c.body()
.
OpenAPIHono
is not chainable after calling thedoc
method because it returnsvoid
This problem can be fixed.
Thanks everyone for tackling this!
Is there any specific problem with the subclass type being Hono?
Not really. We can work around this by swapping the route registration order.
- Cannot infer the correct type for endpoints that may return several response status
- Returning a 204 response yields a compile error
As for these two issues, you should use c.jsonT() instead of c.body().
I tried but jsonT
didn't help unfortunately. I don't want to discuss further here since it's off-topic, but I prepared a reproduction.
@yudai-nkt
Thanks for your response. Okay, If we can work around it, let's do it.
PS.
You can write jsonT({})
to avod the error. This is because null
does not match the expected type {}
. This is confusing and needs to be documented.
To achieve this, we must be able to "infer the type of a subclass in the parent class Hono and pass the types as a generic to it".
Correct me if I'm wrong, but could the subclass just override each of the methods (get
, post
, etc) with the corrected type?
As mentioned above, it is impossible not to treat the Hono
's subclass as Hono
. Closing this issue. Thanks!