首页 > 解决方案 > 在 TypeScript 中导入 Victor.js?

问题描述

我正在尝试在 TypeScript 项目(3.0.1)中使用victor.js库,但我在尝试导入和使用它时感到非常心痛。我已经从 npm 安装了它以及它的类型(victor @types/victor)。我尝试过多种方式导入它,但似乎无法让它与我的 IDE 中的类型解析一起导入。

我试过这些:

import { Victor} from 'victor';  
import * as v from 'victor'; 

(此模块只能通过打开“allowSyntheticDefaultImports”标志并引用其默认导出来引用 ECMAScript 导入/导出)

import Victor = require('victor');  

(针对 ecmascript 模块时有效但不兼容)

const Victor = require("victor");  

(有效导入,我可以构造对象,但不存在任何类型)

我敢肯定,以前有人遇到过与此类似的情况。如果它有助于 victor 的 index.js 的顶部有以下行:

exports = module.exports = Victor;

标签: javascripttypescriptimportmodule

解决方案


In Brief

You're trying to use victor as if it were an es6 module, but it is not. I see two options:

  1. Let tsc convert your modules to a format like commonjs, in which case tsc will provide necessary glue logic between victor and your code

  2. Or you need to load your application through a module loader that provides the glue.

Detailed Explanation

When I run the latest tsc with the import that you show, the error I get is:

This module can only be referenced with ECMAScript imports/exports by turning on the 'esModuleInterop' flag and referencing its default export.

When I turn on esModuleInterop, then it works just fine. Here is the test code I've used:

import Victor from "victor";

const foo = new Victor(1, 2);
console.log(foo.y);

And the tsconfig.json:

{
  "compilerOptions": {
    "esModuleInterop": true
  }
}

The issue originates due to the fact that when you do import Victor from "victor" you are asking for the value that would be exported through an export default... statement, which is a syntax provided by es6 modules. However, victor does export anything that corresponds to export default.... So something has to bridge the gap. With what I've shown above, when you compile, tsc emits this:

"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
exports.__esModule = true;
var victor_1 = __importDefault(require("victor"));
var foo = new victor_1["default"](1, 2);
console.log(foo.y);

Note the __importDefault helper function. It is used whenever the TS code wants to access what a module exports as export default... What it does is check whether the module claims to be an es6 module. An es6 module that wants to export a default value is already correctly structured so there's nothing to do if the module is an es6 module. If the module is not an es6 module, then the helper creates a kind of fake module whose default exported value is the value of the original module.

There's an important caveat since you mention "targeting ecmascript modules". If you use, this tsconfig.json:

{
  "compilerOptions": {
    "esModuleInterop": true,
    "module": "es6"
  }
}

Then the emitted code is:

import Victor from "victor";
var foo = new Victor(1, 2);
console.log(foo.y);

Note that there is no longer any helper function. It is up to the module loader which will load the modules for your application to provide the same logic as provided by __importDefault. If I rename the file to have the mjs extension and run:

$ node --experimental-modules test.mjs

I get this output:

(node:18394) ExperimentalWarning: The ESM module loader is experimental.
2

When using Node with the experimental module support, it provides the same functionality as __importDefault.


When you just use allowSyntheticDefaultImports without using esModuleInterop you are telling the compiler to assume that there will be something in your toolchain that will do the work of __importDefault. So the compiler does not provide a helper. It allows the compilation to proceed, but you are responsible later to use a module loader that will perform the same work as __importDefault.


推荐阅读