fastify / fastify/help

TypeScript overload error soup when registering a route.

Open
#1,110 1 comment 0 reactions 0 assignees View on GitHub
help wanted
Dominant language
No language data
Stars
68
Forks
8
Avg merge
11h 2m
Merged PRs (30d)
2

Description

## Question

After being inspired by this [template](https://github.com/TheB1gFatPanda/fastify-typescript-starter) and liking how the routes are organized, I tried to implement the following routing structure.

```typescript
export interface IRouter {
path: string;

getRoutes: (fastify: FastifyInstance, options: RouteOptions) => Promise;
}
```

It's used to implement "routers" containing multiple route like so:

```typescript
import { FastifyInstance as Instance, RouteOptions as Options } from "fastify";
import { FastifyRequest as Request, FastifyReply as Reply } from "fastify";

import type { IRouter } from "../types.js";

export class GreetRouter implements IRouter {
public path = "/greet";

public async getRoutes(fastify: Instance, options: Options) {
fastify.post(this.path, async function (request: Request<{ Body: { name: string } }>, reply: Reply) {
const { name } = request.body;

const message = `Hello ${name}`;
return reply.send({ message });
});
}
}
```

That interface allows me to define implementations, which contain multiple routes defined with a shorthand such as `fastify.post` and `fastify.get` and later collect them and register them all at once, with options such as the `prefix`.

```typescript
import { FastifyInstance as Instance, FastifyPluginOptions as Options } from "fastify";
import fp from "fastify-plugin";

import { IndexRouter } from "./routers/index.js";
import { GreetRouter } from "./routers/greet.js";
import type { IRouter } from "./types.js";

const routes = fp(async (fastify: Instance, options: Options) => {
const routers: Array = [new IndexRouter(), new GreetRouter()];

routers.forEach((router: IRouter) => {
fastify.log.info(`registering router for ${router.path}`);

fastify.register(router.getRoutes.bind(router), { prefix: "/api/v1" });
});
});

export default routes;
```

All the code in the MRE runs perfectly fine *in watch mode* when using `bun run dev`

Image

However, when attempting to build the project with `tsc` I get the following

Error log when transpiling the project with tsc

$ tsc --build .

src/router.ts:21:22 - error TS2769: No overload matches this call.
Overload 1 of 6, '(plugin: FastifyPluginCallback): FastifyInstance<...> & ... 1 more ... & { ...; }', gave the following error.
Argument of type '(fastify: FastifyInstance, FastifyBaseLogger, FastifyTypeProviderDefault>, options: RouteOptions<...>) => Promise<...>' is not assignable to parameter of type 'FastifyPluginCallback'.
Types of parameters 'options' and 'opts' are incompatible.
Type 'FastifyPluginOptions' is missing the following properties from type 'RouteOptions, RouteGenericInterface, unknown, FastifySchema, FastifyTypeProviderDefault, FastifyBaseLogger>': method, url, handler
Overload 2 of 6, '(plugin: FastifyPluginAsync): FastifyInstance<...> & ... 1 more ... & { ...; }', gave the following error.
Argument of type '(fastify: FastifyInstance, FastifyBaseLogger, FastifyTypeProviderDefault>, options: RouteOptions<...>) => Promise<...>' is not assignable to parameter of type 'FastifyPluginAsync'.
Types of parameters 'options' and 'opts' are incompatible.
Type 'FastifyPluginOptions' is missing the following properties from type 'RouteOptions, RouteGenericInterface, unknown, FastifySchema, FastifyTypeProviderDefault, FastifyBaseLogger>': method, url, handler
Overload 3 of 6, '(plugin: FastifyPluginCallback | FastifyPluginAsync<...> | Promise<...> | Promise<...>): FastifyInstance<...> & ... 1 more ... & { ...; }', gave the following error.
Argument of type '(fastify: FastifyInstance, FastifyBaseLogger, FastifyTypeProviderDefault>, options: RouteOptions<...>) => Promise<...>' is not assignable to parameter of type 'FastifyPluginCallback | FastifyPluginAsync<...> | Promise<...> | Promise<...>'.
Type '(fastify: FastifyInstance, FastifyBaseLogger, FastifyTypeProviderDefault>, options: RouteOptions<...>) => Promise<...>' is not assignable to type 'FastifyPluginCallback'.
Types of parameters 'options' and 'opts' are incompatible.
Type 'FastifyPluginOptions' is missing the following properties from type 'RouteOptions, RouteGenericInterface, unknown, FastifySchema, FastifyTypeProviderDefault, FastifyBaseLogger>': method, url, handler

21 fastify.register(router.getRoutes.bind(router));
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

To my understanding, I'm providing the correct signature when registering the route, but according to this error, `RouteOptions` and `FastifyPluginOptions` are incompatible (which I'm not arguing against).

I don't exactly know how to merge or make these types overlap to make this work even when building and I don't really want to cast the plugin callback to `any`. I'd love to know where have I gone wrong with types.

## MRE

note that in the MRE the `{ prefix: "/api/v1" }` was not added to the options, but the error remains the same.

available here: https://github.com/ClayCore/fastify-type-error

## Your Environment

- *node version*: 24.6.0
- *bun version*: 1.2.19
- *fastify version*: ^5.5.0
- *typescript version*: ^5
- *os*: Linux CachyOS (6.16.3-2-cachyos)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.