You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Turbo单体仓库中Fastify插件的TypeScript类型不匹配问题解决

解决Fastify插件TypeScript类型不匹配问题

问题背景

在Turbo单体仓库中以独立包形式开发Fastify插件,采用TypeScript+Tsup打包,插件运行时功能正常,但在Fastify应用中注册插件时,TypeScript抛出类型不匹配错误:

No overload matches this call.
  Overload 1 of 3, '(plugin: FastifyPluginCallback<{ host: string; port: number; username: string; password: string; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>, opts?: FastifyRegisterOptions<...> | undefined): FastifyInstance<...> & ... 1 more ... & { ...; }', gave the following error.
    Argument of type 'typeof import("/path/to/project/libs/plugins/redis-plugin/dist/index")' is not assignable to parameter of type 'FastifyPluginCallback<{ host: string; port: number; username: string; password: string; }, RawServerDefault, FastifyTypeProvider, FastifyBaseLogger>'.

相关代码片段

插件package.json

{
  "name": "@dekada/redis-plugin",
  "type": "commonjs",
  "version": "1.0.0",
  "description": "A plugin for adding Redis client to Fastify",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "scripts": {
    "build": "tsup",
    "dev": "tsup --watch --onSuccess \"node --env-file=.env.development dist/index.js\""
  },
  "dependencies": {
    "@dekada/redis": "workspace:*",
    "fastify": "^5.0.0",
    "fastify-plugin": "^5.0.1",
    "ioredis": "^5.4.1"
  },
  "devDependencies": {
    "@dekada/typescript": "workspace:*",
    "@types/node": "^22.7.5",
    "tsup": "^8.3.0"
  }
}

Redis插件代码

import type { FastifyInstance, FastifyPluginAsync } from 'fastify';
import { initializeRedis } from '@dekada/redis';
import fp from 'fastify-plugin';

interface RedisPluginOptions {
  host: string;
  port: number;
  username: string;
  password: string;
}

export const redisPlugin: FastifyPluginAsync<RedisPluginOptions> = async function (
  server: FastifyInstance,
  options: RedisPluginOptions
) {
  const redis = initializeRedis({
    host: options.host,
    port: options.port,
    username: options.username,
    password: options.password,
  });

  server.decorate('redis', redis);

  server.addHook('onReady', async () => {
    try {
      await redis.connect();
      server.log.info('Connected to Redis');
    } catch (error) {
      server.log.error(error);
    }
  });
};

export default fp(redisPlugin, { name: 'redisPlugin' });

插件注册代码

import type { FastifyInstance } from 'fastify'
import redisPlugin from '@dekada/redis-plugin'
import fastifyCookie from '@fastify/cookie'
import fastifyCsrf from '@fastify/csrf-protection'
import fastifyHelmet from '@fastify/helmet'
import fastifySession from '@fastify/session'
import Fastify from 'fastify'
import { csrfOptions, helmetOptions, serverOptions, sessionOptions } from './configs'
import authRoutes from './routes'

async function buildServer(): Promise<FastifyInstance> {
  const server = Fastify(serverOptions)

  // Plugins
  await server.register(fastifyHelmet, helmetOptions)
  await server.register(fastifyCookie)
  await server.register(fastifyCsrf, csrfOptions)
  await server.register(fastifySession, sessionOptions)
  await server.register(redisPlugin, { host: 'localhost', port: 6379, username: '', password: '' })

  // Routes
  await server.register(authRoutes, { prefix: 'api/auth' })

  return server
}

export default buildServer

全局tsconfig

{
  "$schema": "https://json.schemastore.org/tsconfig",
  "compilerOptions": {
    "target": "ES2023",
    "module": "ESNext",
    "lib": ["ES2021"],
    "moduleResolution": "node",
    "allowJs": false,
    "skipLibCheck": true,
    "strict": true,
    "strictNullChecks": true,
    "strictPropertyInitialization": true,
    "noImplicitAny": true,
    "noImplicitThis": true,
    "alwaysStrict": true,
    "noFallthroughCasesInSwitch": true,
    "types": ["node"],
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "allowSyntheticDefaultImports": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noUncheckedIndexedAccess": true,
    "noImplicitOverride": true,
    "noPropertyAccessFromIndexSignature": true,
    "exactOptionalPropertyTypes": true,
    "incremental": true,
    "tsBuildInfoFile": "./dist/.tsbuildinfo"
  }
}

Tsup配置

import { defineConfig } from 'tsup'

export default defineConfig(options => ({
  entryPoints: ['src/**/*.ts'],
  clean: true,
  format: ['cjs'],
  dts: true,
  ...options,
}))

解决方案

1. 统一Fastify版本

插件与主应用的Fastify版本必须完全一致,避免类型定义冲突。检查双方package.json,确保fastify版本均为^5.0.0,并在Turbo仓库中通过锁文件锁定版本,防止子包安装不同版本。

2. 修正插件导出逻辑

确保fastify-plugin包装后的函数作为默认导出,避免多余导出导致类型混淆:

import type { FastifyInstance, FastifyPluginAsync } from 'fastify';
import { initializeRedis } from '@dekada/redis';
import fp from 'fastify-plugin';

interface RedisPluginOptions {
  host: string;
  port: number;
  username: string;
  password: string;
}

const redisPlugin: FastifyPluginAsync<RedisPluginOptions> = async (server, options) => {
  const redis = initializeRedis(options);
  server.decorate('redis', redis);

  server.addHook('onReady', async () => {
    try {
      await redis.connect();
      server.log.info('Connected to Redis');
    } catch (error) {
      server.log.error(error);
    }
  });
};

// 直接导出包装后的插件
export default fp(redisPlugin, { name: 'redisPlugin' });

// 导出配置类型供主应用使用
export type { RedisPluginOptions };

3. 优化Tsup打包配置

调整Tsup配置,明确入口文件并确保类型生成正确:

import { defineConfig } from 'tsup'

export default defineConfig(options => ({
  entryPoints: ['src/index.ts'], // 指定单个入口,避免多入口类型混乱
  clean: true,
  format: ['cjs'],
  dts: {
    resolve: true, // 确保依赖类型正确解析
    entry: 'src/index.ts' // 指定类型生成入口
  },
  ...options,
}))

4. 调整模块解析配置

将全局tsconfig的moduleResolution改为nodenext,更好兼容CommonJS与ES模块混合场景:

{
  "compilerOptions": {
    "moduleResolution": "nodenext",
    // 其他配置保持不变
  }
}

5. 清理缓存并重新构建

清理Turbo缓存、node_modules及编译产物,重新构建所有包:

turbo clean
rm -rf node_modules
npm install
turbo build

内容的提问来源于stack exchange,提问作者Nazar Duma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 05:37:02