使用基础导入时TypeScript提示「无法找到模块」的原因排查
问题
搭建基于TypeScript的Apollo GraphQL项目时,编译正常但运行时出现导入错误:
Cannot find module '/Users/czbaker/Projects/express-apollo-ts/dist/gql/resolvers' imported from /Users/czbaker/Projects/express-apollo-ts/dist/main.js
项目相关文件如下:
src/main.ts
import express, { Express, Request, Response } from 'express'; import { ApolloServer } from '@apollo/server'; import { expressMiddleware } from '@apollo/server/express4'; import { ApolloServerPluginDrainHttpServer } from '@apollo/server/plugin/drainHttpServer'; import http, { Server } from 'http'; import path from 'path'; import dotenv from 'dotenv'; // GQL import { resolvers } from './gql/resolvers'; import { typeDefs } from './gql/typeDefs'; // 注意:此处代码缺少app和httpServer的定义,需补充 // const app: Express = express(); // const httpServer: Server = http.createServer(app); // Load config const envpath: string = path.resolve(process.cwd(), '.env'); dotenv.config({ path: envpath, debug: true }); // Create Apollo Server const server = new ApolloServer({ resolvers, typeDefs, plugins: [ApolloServerPluginDrainHttpServer({ httpServer })] }); await server.start(); // Use Middleware app.use(expressMiddleware(server)); // Launch Server await new Promise<void>((resolve) => httpServer.listen({ port: process.env.SERVER_PORT }, resolve)); console.log(`Server available at http://localhost:${process.env.SERVER_PORT}/`);
src/gql/resolvers.ts
export const resolvers: any = { Query: { hello: () => "World!" } }
src/gql/typeDefs.ts
export const typeDefs = `#graphql type Query { hello: String; } `
tsconfig.json
{ "compilerOptions": { "rootDirs": ["src"], "outDir": "dist", "lib": ["es2020"], "target": "es2020", "module": "ES6", "moduleResolution": "node", "esModuleInterop": true, "types": ["node"], }, "include": [ "src/**/*" ], "exclude": [ "node_modules" ] }
package.json
{ "name": "express-apollo-ts", "version": "1.0.0", "description": "Apollo GraphQL server via Express/TypeScript/Mongoose w/ Subscription support", "main": "main.ts", "repository": "https://github.com/czbak3r/express-apollo-ts", "author": "Charles Baker", "license": "MIT", "type": "module", "devDependencies": { "@types/express": "^4.17.14", "@types/node": "^18.11.9", "nodemon": "^2.0.20", "typescript": "^4.9.3" }, "dependencies": { "@apollo/server": "^4.2.1", "dotenv": "^16.0.3", "express": "^4.18.2", "graphql": "^16.6.0", "mongoose": "^6.7.3" }, "scripts": { "dev": "concurrently \"tsc --watch\" \"nodemon -q dist/main.js\"", "build": "tsc", "start": "nodemon ./dist/main.js" } }
编译后dist目录结构:
dist/ ├── main.js └── gql/ ├── resolvers.js └── typeDefs.js
已尝试tsc监听模式和ts-node,结果一致,怀疑和ESM相关,但对ESM不熟悉。
解决方案
核心原因
项目在package.json中设置了"type": "module",Node.js会以**ESM(ECMAScript模块)**模式运行代码。ESM有严格要求:所有相对导入必须包含完整文件扩展名(如.js)。但当前TypeScript编译配置下,编译后的导入语句不会自动添加.js后缀,导致Node.js无法找到对应模块。
具体修复步骤
修改
tsconfig.json配置
将module和moduleResolution调整为适配ESM的模式,让TypeScript自动在编译后的代码中添加正确扩展名:{ "compilerOptions": { "rootDirs": ["src"], "outDir": "dist", "lib": ["es2020"], "target": "es2020", "module": "NodeNext", // 替换原ES6 "moduleResolution": "NodeNext", // 替换原node "esModuleInterop": true, "types": ["node"], "resolveJsonModule": true // 可选,若需导入json文件 }, "include": ["src/**/*"], "exclude": ["node_modules"] }补充main.ts中缺失的变量定义
代码中未定义app和httpServer,会导致运行时额外报错,需补充:const app: Express = express(); const httpServer: Server = http.createServer(app);(可选)优化TypeScript导入写法
使用NodeNext模块模式时,TypeScript允许在源码中用.ts后缀导入文件,编译时会自动替换为.js:// 原导入 import { resolvers } from './gql/resolvers'; // 可改为(NodeNext模式下两种写法都支持) import { resolvers } from './gql/resolvers.ts';
验证修复
重新执行npm run build,查看dist/main.js中的导入语句,会自动变为:
import { resolvers } from './gql/resolvers.js'; import { typeDefs } from './gql/typeDefs.js';
此时运行npm run start,导入错误即可解决。
内容的提问来源于stack exchange,提问作者MisutoWolf
相关产品推荐
相关产品推荐

