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

Incorrect client RPC response inference for Cloudflare Workers RPC return types which stub Arrays #3811

Open
a-type opened this issue Jan 8, 2025 · 1 comment
Labels

Comments

@a-type
Copy link

a-type commented Jan 8, 2025

What version of Hono are you using?

4.6.15

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

Cloudflare Workers

What steps can reproduce the bug?

Unfortunately both products use "RPC" naming here, so I'll try to be clear about which is which.

As recommended in Cloudflare Workers RPC docs for Typescript, I am specifying my bindings types using the Service<T> wrapper type to generate proper 'stub' typings which enforce requirements related to chained RPC methods becoming async, for example.

class PublicApiService extends WorkerEntrypoint {
  async getList() {
    return [{ name: 'foo' }, { name: 'bar' }];
  }
}

// in the main API worker --

interface Bindings {
  PUBLIC_STORE: Service<PublicApiService>;
}

const app = new Hono<{ Bindings: Bindings }>()
  .get('/list', async (ctx) => {
    const list = await ctx.env.PUBLIC_STORE.getList();
    return ctx.json(list);
  });

So far I have only encountered issues with return types which were Arrays before wrapping with Service.

What is the expected behavior?

After returning the Array-based data via ctx.json, Hono's RPC typings would correctly infer the response typing as a native Array on the client-side.

What do you see instead?

When using this wrapper type, Hono RPC cannot correctly infer typings of responses for Arrays. Instead, it produces a messy object type on the other end:

type ListResp = {
    [x: number]: {
        name: string;
    };
    ... 40 more ...;
    readonly [Symbol.unscopables]: {
        [x: number]: boolean | undefined;
        length?: boolean | undefined;
        toString?: boolean | undefined;
        toLocaleString?: boolean | undefined;
        pop?: boolean | undefined;
        push?: boolean | undefined;
        concat?: boolean | undefined;
        join?: boolean | undefined;
        reverse?: boolean | undefined;
        shift?: boolean | undefined;
        slice?: boolean | undefined;
        sort?: boolean | undefined;
        splice?: boolean | undefined;
        unshift?: boolean | undefined;
        indexOf?: boolean | undefined;
        lastIndexOf?: boolean | undefined;
        every?: boolean | undefined;
        some?: boolean | undefined;

... and so on

Additional information

Before passing the response to ctx.json, inspecting the type which the Service<T> wrapper has created looks something like this:

const list: {
    name: string;
}[] & Disposable

Disposable is defined in @cloudflare/workers-types as an empty interface, and within standard lib as an object with a key on Symbol.dispose. Since this type is otherwise an Array, I wonder if merely this intersection is undermining Hono RPC's typings, and if that could be accounted for by greedily looking for Array inheritance and forcing the RPC response type to an array in such cases?

Workaround

Removing the Service<T> wrapper on my RPC service bindings resolves the client typing inference problem, but exposes me to mistakes on the server-side regarding Cloudflare's RPC usage. This is an acceptable tradeoff for me a the moment, but it would be nice to have both sides correctly typed.

@a-type a-type added the triage label Jan 8, 2025
@yusukebe
Copy link
Member

yusukebe commented Jan 9, 2025

Hi @a-type

I think it's not an actual bug of hono since it's not expected c.json() accepts Disposable. We might be able to support it, but the definition of the type will be more complicated.

@yusukebe yusukebe added not bug and removed triage labels Jan 9, 2025
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

2 participants