fastify / fastify-auth

Run multiple auth functions in Fastify
Other
332 stars 56 forks source link

Typescript bug when used in conjunction with "fastify-type-provider-zod". #220

Open geovla93 opened 5 months ago

geovla93 commented 5 months ago

Prerequisites

Fastify version

4.25.2

Plugin version

4.4.0

Node.js version

20.9.0

Operating system

macOS

Operating system version (i.e. 20.04, 11.3, 10)

14.2.1

Description

I came across a weird bug today when I was building my API. Basically when using @fastify/auth plugin along with fastify-type-provider-zod package typescript complains. I get the following error:

Type 'preHandlerHookHandler<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, RouteGenericInterface, unknown, FastifySchema, FastifyTypeProviderDefault, FastifyBaseLogger>' is not assignable to type 'onRequestHookHandler<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, RouteGenericInterface, unknown, FastifySchema, ZodTypeProvider, FastifyBaseLogger> | onRequestHookHandler<...>[] | undefined'.
  Type 'preHandlerHookHandler<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, RouteGenericInterface, unknown, FastifySchema, FastifyTypeProviderDefault, FastifyBaseLogger>' is not assignable to type 'onRequestHookHandler<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, RouteGenericInterface, unknown, FastifySchema, ZodTypeProvider, FastifyBaseLogger>'.
    The 'this' types of each signature are incompatible.
      Type 'FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, ZodTypeProvider>' is not assignable to type 'FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, FastifyTypeProviderDefault>'.
        The types returned by 'after()' are incompatible between these types.
          Type 'FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, ZodTypeProvider> & PromiseLike<...>' is not assignable to type 'FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, FastifyTypeProviderDefault> & PromiseLike<...>'.
            Type 'FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, ZodTypeProvider> & PromiseLike<...>' is not assignable to type 'FastifyInstance<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, FastifyBaseLogger, FastifyTypeProviderDefault>'.
              The types of 'addSchema(...).addHook' are incompatible between these types.
                Type '{ <RouteGeneric extends import("/Users/geovla/Developer/Projects/apps/taskmaster/api/node_modules/.pnpm/fastify@4.26.0/node_modules/fastify/types/route").RouteGenericInterface = import("/Users/geovla/Developer/Projects/apps/taskmaster/api/node_modules/.pnpm/fastify@4.26.0/node_modules/fastify/types/route").RouteGene...' is not assignable to type '{ <RouteGeneric extends import("/Users/geovla/Developer/Projects/apps/taskmaster/api/node_modules/.pnpm/fastify@4.26.0/node_modules/fastify/types/route").RouteGenericInterface = import("/Users/geovla/Developer/Projects/apps/taskmaster/api/node_modules/.pnpm/fastify@4.26.0/node_modules/fastify/types/route").RouteGene...'. Two different types with this name exist, but they are unrelated.
                  Types of parameters 'hook' and 'hook' are incompatible.
                    Types of parameters 'opts' and 'opts' are incompatible.
                      Type 'RouteOptions<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, any, any, any, ZodTypeProvider, FastifyBaseLogger> & { ...; }' is not assignable to type 'RouteOptions<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, any, any, any, FastifyTypeProviderDefault, FastifyBaseLogger> & { ...; }'.
                        Type 'RouteOptions<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, any, any, any, ZodTypeProvider, FastifyBaseLogger> & { ...; }' is not assignable to type 'RouteOptions<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, any, any, any, FastifyTypeProviderDefault, FastifyBaseLogger>'.
                          Types of property 'handler' are incompatible.
                            Type 'RouteHandlerMethod<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, any, any, any, ZodTypeProvider, FastifyBaseLogger>' is not assignable to type 'RouteHandlerMethod<RawServerDefault, IncomingMessage, ServerResponse<IncomingMessage>, any, any, any, FastifyTypeProviderDefault, FastifyBaseLogger>'.
                              Type 'FastifyTypeProviderDefault' is not assignable to type 'ZodTypeProvider'.

The error is shown both in onRequest and preHandler hooks. I should mention that the error does not show up if I remove the app.auth() function and use the auth functions on their own or in an array.

Steps to Reproduce

Create a fastify server and install the mentioned packages along with zod. Create two auth functions and try to use them in either onRequest or preHandler hooks.

export default async function routes(app: FastifyInstance) {
  app
    .withTypeProvider<ZodTypeProvider>()
    .route({
      method: 'GET',
      url: '/',
      schema: {
        description: 'Get all users',
        tags: ['User'],
        response: {
          200: userResponseSchema.array(),
        },
      },
      onRequest: app.auth([app.authenticate, app.guard]),
      handler: getUsersHandler,
    });
}

Expected Behavior

I would like to use this package without this typescript error like this:

onRequest: app.auth([app.authenticate, app.guard])
mcollina commented 5 months ago

I have no clue why this is happening unfortunately. A PR to fix would be awesome.

geovla93 commented 5 months ago

Great I will fork the repo and play around with the type definitions. If I come up with a fix I will add a PR. Thanks for the feedback!

geovla93 commented 5 months ago

Just want to update you on this. Apparently, this issue exists with other type providers as well as you can see in this issue on the fastify-type-provider-typebox package. It seems the problem is the return type of the fastify.auth([]) which is typed with the FastifyTypeProvider and not the specific type provider used in the project. Unfortunately, I'm not that proficient in typescript to fix this so for now I will use the suggestion in the issue to add as any wherever I use fastify.auth([]).

vinayakakv commented 3 months ago

I was poking around with index-test.d.ts and found a few things:

The passing case:

jsonSchemaToTS.route({
  method: 'GET',
  url: '/with-param',
  schema: {body: {type: 'object', properties: {param: {type: 'string'}}, required: ['param']}},
  preHandler: jsonSchemaToTS.auth([]),
  handler: (req) => {
    expectType<{ param: string, [x: string]: unknown }>(req.body)
  }
})

The failing case (req.body is of type unknown):

jsonSchemaToTS.get("/test", {
  preHandler: jsonSchemaToTS.auth([]),
  schema: { body: { type: 'object', properties: { param: { type: 'string' } }, required: ['param'] } },
}, (req) => {
    expectType<{ param: string, [x: string]: unknown }>(req.body)
})

As mentioned by @geovla93, the failing case starts to work if we do preHandler: jsonSchemaToTS.auth([]) as any

Update: just found that this also works

jsonSchemaToTS.get("/test", {
  preHandler: jsonSchemaToTS.auth([]),
  schema: { body: { type: 'object', properties: { param: { type: 'string' } }, required: ['param'] } },
  handler: (req) => {
    expectType<{ param: string, [x: string]: unknown }>(req.body)
  }
})
bcomnes commented 1 month ago

I was experiencing a similar issue with fastify-type-provider-json-schema-to-ts where when the auth plugin was used in the preHandler, everything was resolving to unknown. I'm not quite sure what fixed it, but ensuring I was running 4.6.1 seemed to resolve the issue for me.