fastify / restartable

Restart Fastify without losing a request
MIT License
104 stars 8 forks source link

How to properly type an http2 restartable instance #63

Closed denchen closed 5 months ago

denchen commented 8 months ago

Prerequisites

Fastify version

4.26.0

Plugin version

2.2.0

Node.js version

20.9.0

Operating system

macOS

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

14.2.1

Description

Restartable doesn't seem to take a generic argument, so I'm wondering how to properly type an HTTP/2 server.

Steps to Reproduce

The following gives a TypeScript error:

  const app = await restartable<Fastify>(async (factory, opts) => factory(opts), { logger: true, http2: true });
Object literal may only specify known properties, and 'http2' does not exist in type 'FastifyServerOptions'.

http2 only exists on FastifyHttp2SecureOptions, which extends FastifyServerOptions, but I don't see the proper way of telling restartable that. If I create a helper function that instantiates the Fastify instance with the right types, I'll get a different (but related?) error:

  async function createApp(
    fastify: Fastify,
    opts: FastifyHttp2SecureOptions<Http2SecureServer, FastifyBaseLogger>
  ) {
    const app = fastify(opts);

    return app;
  }

  const app = await restartable(createApp, { logger: true, http2: true });
Argument of type '(fastify: typeof import("/Users/denchen/git/ui/node_modules/fastify/fastify.d.ts"), opts: FastifyHttp2SecureOptions<Http2SecureServer, FastifyBaseLogger>) => Promise<...>' is not assignable to parameter of type 'ApplicationFactory'.
  Types of parameters 'opts' and 'opts' are incompatible.
    Type 'FastifyServerOptions' is not assignable to type 'FastifyHttp2SecureOptions<Http2SecureServer, FastifyBaseLogger>'.
      Type 'FastifyServerOptions' is not assignable to type 'FastifyServerOptions<Http2SecureServer, FastifyBaseLogger>'.
        Types of property 'logger' are incompatible.
          Type 'boolean | FastifyBaseLogger | (FastifyLoggerOptions<RawServerDefault, FastifyRequest<RouteGenericInterface, RawServerDefault, ... 5 more ..., ResolveFastifyRequestType<...>>, FastifyReply<...>> & PinoLoggerOptions) | undefined' is not assignable to type 'boolean | FastifyBaseLogger | (FastifyLoggerOptions<Http2SecureServer, FastifyRequest<RouteGenericInterface, Http2SecureServer, ... 5 more ..., ResolveFastifyRequestType<...>>, FastifyReply<...>> & PinoLoggerOptions) | undefined'.
            Type 'FastifyLoggerOptions<RawServerDefault, FastifyRequest<RouteGenericInterface, RawServerDefault, IncomingMessage, FastifySchema, FastifyTypeProviderDefault, unknown, FastifyBaseLogger, ResolveFastifyRequestType<...>>, FastifyReply<...>> & PinoLoggerOptions' is not assignable to type 'boolean | FastifyBaseLogger | (FastifyLoggerOptions<Http2SecureServer, FastifyRequest<RouteGenericInterface, Http2SecureServer, ... 5 more ..., ResolveFastifyRequestType<...>>, FastifyReply<...>> & PinoLoggerOptions) | undefined'.
              Type 'FastifyLoggerOptions<RawServerDefault, FastifyRequest<RouteGenericInterface, RawServerDefault, IncomingMessage, FastifySchema, FastifyTypeProviderDefault, unknown, FastifyBaseLogger, ResolveFastifyRequestType<...>>, FastifyReply<...>> & PinoLoggerOptions' is not assignable to type 'FastifyLoggerOptions<Http2SecureServer, FastifyRequest<RouteGenericInterface, Http2SecureServer, Http2ServerRequest, ... 4 more ..., ResolveFastifyRequestType<...>>, FastifyReply<...>> & PinoLoggerOptions'.
                Type 'FastifyLoggerOptions<RawServerDefault, FastifyRequest<RouteGenericInterface, RawServerDefault, IncomingMessage, FastifySchema, FastifyTypeProviderDefault, unknown, FastifyBaseLogger, ResolveFastifyRequestType<...>>, FastifyReply<...>> & PinoLoggerOptions' is not assignable to type 'FastifyLoggerOptions<Http2SecureServer, FastifyRequest<RouteGenericInterface, Http2SecureServer, Http2ServerRequest, ... 4 more ..., ResolveFastifyRequestType<...>>, FastifyReply<...>>'.
                  Types of property 'serializers' are incompatible.
                    Type '({ req?: ((req: FastifyRequest<RouteGenericInterface, RawServerDefault, IncomingMessage, FastifySchema, FastifyTypeProviderDefault, unknown, FastifyBaseLogger, ResolveFastifyRequestType<...>>) => { ...; }) | undefined; err?: ((err: FastifyError) => { ...; }) | undefined; res?: ((res: ResSerializerReply<...>) => { .....' is not assignable to type '{ req?: ((req: FastifyRequest<RouteGenericInterface, Http2SecureServer, Http2ServerRequest, FastifySchema, FastifyTypeProviderDefault, unknown, FastifyBaseLogger, ResolveFastifyRequestType<...>>) => { ...; }) | undefined; err?: ((err: FastifyError) => { ...; }) | undefined; res?: ((res: ResSerializerReply<...>) => {...'.
                      Type '{ req?: ((req: FastifyRequest<RouteGenericInterface, RawServerDefault, IncomingMessage, FastifySchema, FastifyTypeProviderDefault, unknown, FastifyBaseLogger, ResolveFastifyRequestType<...>>) => { ...; }) | undefined; err?: ((err: FastifyError) => { ...; }) | undefined; res?: ((res: ResSerializerReply<...>) => { ......' is not assignable to type '{ req?: ((req: FastifyRequest<RouteGenericInterface, Http2SecureServer, Http2ServerRequest, FastifySchema, FastifyTypeProviderDefault, unknown, FastifyBaseLogger, ResolveFastifyRequestType<...>>) => { ...; }) | undefined; err?: ((err: FastifyError) => { ...; }) | undefined; res?: ((res: ResSerializerReply<...>) => {...'.
                        Type '{ req?: ((req: FastifyRequest<RouteGenericInterface, RawServerDefault, IncomingMessage, FastifySchema, FastifyTypeProviderDefault, unknown, FastifyBaseLogger, ResolveFastifyRequestType<...>>) => { ...; }) | undefined; err?: ((err: FastifyError) => { ...; }) | undefined; res?: ((res: ResSerializerReply<...>) => { ......' is not assignable to type '{ req?: ((req: FastifyRequest<RouteGenericInterface, Http2SecureServer, Http2ServerRequest, FastifySchema, FastifyTypeProviderDefault, unknown, FastifyBaseLogger, ResolveFastifyRequestType<...>>) => { ...; }) | undefined; err?: ((err: FastifyError) => { ...; }) | undefined; res?: ((res: ResSerializerReply<...>) => {...'.
                          Types of property 'req' are incompatible.
                            Type '((req: FastifyRequest<RouteGenericInterface, RawServerDefault, IncomingMessage, FastifySchema, FastifyTypeProviderDefault, unknown, FastifyBaseLogger, ResolveFastifyRequestType<...>>) => { ...; }) | undefined' is not assignable to type '((req: FastifyRequest<RouteGenericInterface, Http2SecureServer, Http2ServerRequest, FastifySchema, FastifyTypeProviderDefault, unknown, FastifyBaseLogger, ResolveFastifyRequestType<...>>) => { ...; }) | undefined'.
                              Type '(req: FastifyRequest<RouteGenericInterface, RawServerDefault, IncomingMessage, FastifySchema, FastifyTypeProviderDefault, unknown, FastifyBaseLogger, ResolveFastifyRequestType<...>>) => { ...; }' is not assignable to type '(req: FastifyRequest<RouteGenericInterface, Http2SecureServer, Http2ServerRequest, FastifySchema, FastifyTypeProviderDefault, unknown, FastifyBaseLogger, ResolveFastifyRequestType<...>>) => { ...; }'.
                                Types of parameters 'req' and 'req' are incompatible.
                                  Type 'FastifyRequest<RouteGenericInterface, Http2SecureServer, Http2ServerRequest, FastifySchema, FastifyTypeProviderDefault, unknown, FastifyBaseLogger, ResolveFastifyRequestType<...>>' is not assignable to type 'FastifyRequest<RouteGenericInterface, RawServerDefault, IncomingMessage, FastifySchema, FastifyTypeProviderDefault, unknown, FastifyBaseLogger, ResolveFastifyRequestType<...>>'.
                                    Type 'Http2ServerRequest' is missing the following properties from type 'IncomingMessage': headersDistinct, trailersDistinct

Expected Behavior

No response

mcollina commented 8 months ago

Thanks for reporting! Would you like to send a Pull Request to address this issue? Remember to add unit tests.

denchen commented 8 months ago

@mcollina Let me first say that I'm far from an expert at Typescript, but I'm giving it a shot. My first few attempts at introducing generics didn't work at all, until I finally decided to introduce overloading similar to how the fastify() types are overloaded here:

https://github.com/fastify/fastify/blob/3a646b863be964d1008785e7318ffe964aac6aff/fastify.d.ts#L208-L238

The overloading worked for at least my use case (for both default fastify and fastify with http2, but I'm not at all confident that it'll work for everyone else, nor am I sure if overloading is the only way to go about it. Thus far, I'm only modifying types/index.d.ts and none of the actual code. I can still create a PR after I try to add a test or two, but it'll probably be a couple days from now.

mcollina commented 8 months ago

modifying the types is ok. Also add a test for the type, we use tsd.

Fdawgs commented 5 months ago

Closed by #65