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

Webpack与Babel无法解析TypeScript模块问题求助

模块导入问题:无后缀导入.ts文件失败,加后缀则成功

问题现象

从src目录下的services文件夹导入Config模块时,使用import Config from './services/Config';会触发模块找不到错误,但添加.ts后缀写成./services/Config.ts就能正常导入。

报错信息

Module build failed (from ./node_modules/babel-loader/lib/index.js):
Error: Cannot find module './services/Config' from 'D:\github\repoName\src'

已确认Config文件存在于services文件夹中,且services文件夹与index.js文件在同一目录。

配置文件内容

tsconfig.json

{
  "compilerOptions": {
    /* Basic Options */
    "target": "ES6", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */
    "module": "NodeNext", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
    "lib": [
      "es2020",
      "DOM"
    ], /* Specify library files to be included in the compilation. */
    "allowJs": true, /* Allow javascript files to be compiled. */
    "resolveJsonModule": true,
    /* Basic Options */
    "declaration": true, /* Generates corresponding '.d.ts' file. */
    "emitDeclarationOnly": true, /* Generates '.d.ts' file only. */
    "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
    "outDir": "./dist", /* Redirect output structure to the directory. */
    /* Strict Type-Checking Options */
    "strict": true, /* Enable all strict type-checking options. */
    "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
    "strictNullChecks": true, /* Enable strict null checks. */
    "strictFunctionTypes": true, /* Enable strict checking of function types. */
    "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
    "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
    "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
    "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
    /* Additional Checks */
    "noUnusedLocals": true, /* Report errors on unused locals. */
    "noUnusedParameters": true, /* Report errors on unused parameters. */
    "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
    "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
    /* Module Resolution Options */
    "moduleResolution": "NodeNext", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
    "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
    "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
  },
  "include": [
    "src/**/*",
  ],
  "exclude": [
    "./node_modules"
  ],
}

webpack.config.js

const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
//const { PyodidePlugin } = require("@pyodide/webpack-plugin");
const webpack = require('webpack');
const fs = require('fs'); // to check if the file exists
const dotenv = require('dotenv');
const TerserPlugin = require("terser-webpack-plugin");
const WebpackObfuscator = require('webpack-obfuscator');


module.exports = (env) => {
    const currentPath = path.join(__dirname);
    const basePath = currentPath + '/.env';
    const envPath = basePath + '.' + env.ENVIRONMENT;
    const finalPath = fs.existsSync(envPath) ? envPath : basePath;
    const fileEnv = dotenv.config({ path: finalPath }).parsed;

    const envKeys = Object.keys(fileEnv).reduce((prev, next) => {
        prev[`process.env.${next}`] = JSON.stringify(fileEnv[next]);
        return prev;
      }, {});
    
    return {
        entry: path.join(__dirname, 'src', 'index.js'),
        optimization: {
            minimize: true,
            minimizer: [
                new TerserPlugin({
                    test: /\.js(\?.*)?$/i,
                }),
            ],
        },
        output: {
            path: path.resolve(__dirname, "dist"),
            filename: "index.js",
        },
        mode: "development",
        module: {
            rules: [
                {
                    test: /\.(js|jsx)$/,
                    use: "babel-loader",
                    exclude: /node_modules/,
                },
                {
                    test: /\.css$/,
                    use: ['style-loader', 'css-loader']
                },
                {
                    test: /\.(ts|tsx)$/,
                    exclude: /node_modules/,
                    use: {
                    loader: "ts-loader",
                    },
                },            
                {
                    test: /\.(woff(2)?|ttf|eot|png|svg|jpg|gif)(\?v=\d+\.\d+\.\d+)?$/,
                    use: [
                    {
                        loader: "file-loader",
                        options: {
                        name: "[name].[hash:8].[ext]",
                        outputPath: "assets/",
                        },
                    },
                    ],
                },
                {
                    test: /\.s[ac]ss$/i,
                    use: [
                    // Creates `style` nodes from JS strings
                    "style-loader",
                    // Translates CSS into CommonJS
                    "css-loader",
                    // Compiles Sass to CSS
                    "sass-loader",
                    ],
                },
                {
                    test: /\.js$/,
                    enforce: 'post',
                    use: {
                        loader: WebpackObfuscator.loader,
                        options: {
                            rotateStringArray: true

                        }
                    },
                    include: [
                        path.resolve(__dirname, 'src/stores/SimulationStore.js'),
                        path.resolve(__dirname, 'src/stores/MapStore.js'),
                        path.resolve(__dirname, 'src/stores/DevprojectsStore.js'),
                        path.resolve(__dirname, 'src/stores/SimulationResultsStore/SimulationResultsStore.js'),
                        path.resolve(__dirname, 'src/stores/AmenitiesStore.js'),
                        path.resolve(__dirname, 'src/services/market_equilibrium_simulator.js'),
                    ]
                }
            ]
        },
        plugins: [
        //new PyodidePlugin(),
            new HtmlWebpackPlugin({
                template: path.join(__dirname, 'src', 'index.html')
            }),
            new webpack.DefinePlugin(envKeys),
            // new CopyWebpackPlugin({
            //    patterns: [{ from: 'public' }]
            // }),
        ],
        resolve: {
            alias: {
                "@": path.resolve(__dirname, "src/"),
            },
            extensions: [".js", ".jsx", ".ts", ".tsx"],
        },
        devServer: {
            historyApiFallback: true,
            port: 3001,
            open: true,
            // host: '192.168.0.142',//your ip address
            // port: 3001,
            // disableHostCheck: true
        allowedHosts: 'all'
        }
    }
};

.babelrc

{
    "presets": ["@babel/env", "@babel/react", "@babel/preset-typescript"],
    "plugins": [
        ["@babel/plugin-proposal-decorators", { "legacy": true }],
        ["@babel/plugin-proposal-class-properties", { "loose": false }],
        // In contrast to MobX 4/5, "loose" must be false!    ^
        ["babel-plugin-inline-json", {}]
    ]
}

.babel.config.js

module.exports = {
    presets: [['@babel/preset-env', { targets: { node: 'current' } }], "@babel/preset-typescript"],
    plugins: [['@babel/plugin-proposal-decorators', { legacy: true }]]
};

解决方案

原因分析

问题根源在于:

  1. Babel处理JS文件时,默认不会自动解析.ts后缀的模块,而入口文件是JS,由babel-loader优先编译,导致它找不到无后缀的.ts模块。
  2. tsconfig中module和moduleResolution设为NodeNext,该模式严格遵循ES模块规范,不会自动补全文件后缀,与webpack、babel的解析逻辑未对齐。

具体修复步骤

  1. 调整webpack的resolve.extensions顺序
    把.ts/.tsx放在.js/.jsx前面,确保webpack优先解析TypeScript文件:
resolve: {
    alias: {
        "@": path.resolve(__dirname, "src/"),
    },
    extensions: [".ts", ".tsx", ".js", ".jsx"], // 调整顺序
},
  1. 给babel-loader添加后缀解析配置
    让babel在处理JS文件时,能识别.ts后缀的模块:
{
    test: /\.(js|jsx)$/,
    use: {
        loader: "babel-loader",
        options: {
            resolve: {
                extensions: [".js", ".jsx", ".ts", ".tsx"]
            }
        }
    },
    exclude: /node_modules/,
},
  1. 统一模块解析规范(可选)
    如果不需要严格的NodeNext模式,可修改tsconfig让TypeScript适配打包工具的解析逻辑:
"module": "ESNext",
"moduleResolution": "bundler", // 适配webpack等打包工具的解析规则

验证

修改配置后重启webpack dev server,使用import Config from './services/Config';即可正常导入模块。

内容的提问来源于stack exchange,提问作者Lautaro Aguilar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 21:06:00