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
相关产品推荐
相关产品推荐

