blomqma / next-rest-framework

Type-safe, self-documenting APIs for Next.js
https://next-rest-framework.vercel.app
Other
134 stars 17 forks source link

False negative type error for async middleware #163

Closed markedwards closed 3 months ago

markedwards commented 4 months ago

There seems to be a subtle problem with the typing of RouteMiddleware if the function is async, and it can return both a NextResponse and some custom options.

Example:

import { TypedNextResponse, route, routeOperation } from "next-rest-framework";

async function sleep() {
  await new Promise(() => setTimeout(() => {}, 1000));
}

export const { GET } = route({
  hello: routeOperation({
    method: "GET",
  })
    .middleware(async () => {
      await sleep();
      if (Math.random() > 0.5) {
        return {
          foo: "bar",
        };
      }
      return TypedNextResponse.json(
        {
          message: "",
        },
        {
          status: 400,
        },
      );
    })
    .handler((_req, _ctx, { foo }) => {
      console.log(foo);
    }),
});

This raises Type '{ foo: string; }' is not assignable to type 'void | NextResponse<unknown> | TypedNextResponseType<unknown, number, AnyContentTypeWithAutocompleteForMostCommonOnes>'. ts(2345). Also, the foo argument in the handler is unknown.

If the function is made sync (and the await sleep() line is removed, there's no type error. So it seems to be some unimplemented type condition in the async case.

blomqma commented 3 months ago

Thanks, this should be fixed in v6.0.0.

markedwards commented 3 months ago

Confirmed this is fixed. One slightly odd thing is I have to wrap my object like so:

      return {
        ...obj,
      };

Otherwise I get Type 'Foo' is not assignable to type 'BaseOptions'. Index signature for type 'string' is missing in type 'Foo'. Presumably because the object is just described by an interface and does not inherit from Record<string, unknown>?

Otherwise it works great though. Thanks!