有没有办法在 TS 文件中结合 ES6 导入和 commonJS require ?

wro*_*yte 8 javascript typescript

我已经将 NodeJS 项目的代码更改为 Typescript。一切工作正常,除了显然第三方包(文件类型,https://www.npmjs.com/package/file-type)似乎不接受require编译.js文件中生成的。要改变这一点,我必须将 tsconfig.json 的“module”属性更改为“commonjs”以外的另一个值。然而,它破坏了代码并产生了很多问题。

我的 tsconfig.json:

{
    "compilerOptions": {
        "target": "es6",
        "module": "commonjs",
        "allowJs": true,
        "lib": ["ES6"],
        "esModuleInterop": true,
        "moduleResolution": "node",
        "outDir": "build",
        "rootDir": "src",
        "skipLibCheck": true,
        "strict": true
    }
}
Run Code Online (Sandbox Code Playgroud)

我得到的错误:

const filetype = __importStar(require("file-type"));
                              ^

Error [ERR_REQUIRE_ESM]: require() of ES Module C:\Users\user\Desktop\repos\art-api\node_modules\file-type\index.js from C:\Users\user\Desktop\repos\art-api\build\middlewares\process-image.js not supported.
Instead change the require of index.js in C:\Users\user\Desktop\repos\art-api\build\middlewares\process-image.js to a dynamic import() which is available in all CommonJS modules.
    at Object.<anonymous> (C:\Users\user\Desktop\repos\art-api\build\middlewares\process-image.js:33:31)
    at Object.<anonymous> (C:\Users\user\Desktop\repos\art-api\build\controllers\artworkControllers.js:19:25)
    at Object.<anonymous> (C:\Users\user\Desktop\repos\art-api\build\routers\artworkRouter.js:7:30)
    at Object.<anonymous> (C:\Users\user\Desktop\repos\art-api\build\server.js:9:41) {
  code: 'ERR_REQUIRE_ESM'
}
Run Code Online (Sandbox Code Playgroud)

显然,问题在于 JavaScript 中生成的代码与使用 ES6 导出语法的包的代码冲突。如果这是正确的,我该如何解决这个问题?有没有办法只为这个特定的包生成带有导入语法的 .js 代码,或者类似的解决方法?代码的其他部分不会给我带来任何问题,只有这个包(文件类型)的导入。

以防万一,这是“文件类型”的index.js,其中包含编译器抱怨的导入:

import * as strtok3 from 'strtok3';
import {fileTypeFromTokenizer} from './core.js';

export async function fileTypeFromFile(path) {
    const tokenizer = await strtok3.fromFile(path);
    try {
        return await fileTypeFromTokenizer(tokenizer);
    } finally {
        await tokenizer.close();
    }
}

export * from './core.js';

Run Code Online (Sandbox Code Playgroud)

wro*_*yte 3

简短回答:不;您必须选择 CommonJS 或 ES6 模块。

好吧,经过一些研究,我能够理解并解决这个问题。

关键是我正在使用的包file-type不向后兼容 CommonJSrequire()语法。因此,设置时

"module": "commonJS"
Run Code Online (Sandbox Code Playgroud)

在我的tsconfig.json文件中,等效的转译 JavaScript 文件将使用require(); 更具体地说,这样:

const file_type_1 = __importDefault(require("file-type"));
Run Code Online (Sandbox Code Playgroud)

或者一些变体,例如:

const filetype = __importStar(require("file-type"));
Run Code Online (Sandbox Code Playgroud)

它与使用默认导出的index.js文件file-type发生冲突,正如您在与我的问题一起发布的代码中看到的那样。

因此,当我打算继续使用这个包时,我必须更改文件的整个导入/导出系统,其中涉及以下步骤:

1 - 更改"module": "commonJS""module": "ES6"打开tsconfig.json

它可能会导致诸如“无法在模块外部使用 import 语句”之类的错误。发生这种情况是因为我们刚刚更改了转译的 .js 文件的语法,现在的import/export语法而不是require(). 为了让 NodeJS 处理这个问题,我们必须执行第二步:

2 -"type":"module"添加package.json

3 - 相对进口的变更延伸

第二步将解决第一个问题,但是我们在导入文件时必须使用完整路径 - 即添加.js相对导入。所以,

import getImageRouter from './routers/imageRouter';
Run Code Online (Sandbox Code Playgroud)

变成

import getImageRouter from './routers/imageRouter.js';
Run Code Online (Sandbox Code Playgroud)

是的,尽管看起来很奇怪,但我们在导入中使用了 .js 扩展名。Typescript 会足够智能,在编译期间为我们解决这个问题。

这些步骤为我解决了这个问题。

进一步阅读的参考资料:

属性的精彩解释tsconfig.json https://medium.com/@tommedema/typescript-confusion-tsconfig-json-module-moduleresolution-target-lib-explained-65db2c44b491

Node.js 中的 ECMAScript 模块: https://www.typescriptlang.org/docs/handbook/esm-node.html

有关导入带有 .js 扩展名的文件的更多信息: Appending .js extension onrelative import statements during Typescript Compilation (ES6 module)