fastify-auth icon indicating copy to clipboard operation
fastify-auth copied to clipboard

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

Open geovla93 opened this issue 1 year ago • 5 comments

Prerequisites

  • [X] I have written a descriptive issue title
  • [X] I have searched existing issues to ensure the bug has not already been reported

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/[email protected]/node_modules/fastify/types/route").RouteGenericInterface = import("/Users/geovla/Developer/Projects/apps/taskmaster/api/node_modules/.pnpm/[email protected]/node_modules/fastify/types/route").RouteGene...' is not assignable to type '{ <RouteGeneric extends import("/Users/geovla/Developer/Projects/apps/taskmaster/api/node_modules/.pnpm/[email protected]/node_modules/fastify/types/route").RouteGenericInterface = import("/Users/geovla/Developer/Projects/apps/taskmaster/api/node_modules/.pnpm/[email protected]/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])

geovla93 avatar Jan 31 '24 20:01 geovla93

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

mcollina avatar Feb 01 '24 09:02 mcollina

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 avatar Feb 01 '24 14:02 geovla93

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([]).

geovla93 avatar Feb 01 '24 19:02 geovla93

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)
  }
})

vinayakakv avatar Apr 06 '24 05:04 vinayakakv

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.

bcomnes avatar Jun 18 '24 00:06 bcomnes