Node.js + TypeScript: 类型脚本编译代码的语法不清楚

28

我正在尝试在我的Node项目中使用TypeScript,但是我遇到了一些问题。

这是我的index.ts文件:

import express from 'express';

const app = express();

我正在运行:

tsc --module commonsjs -d index.ts

我的输出是index.js

var express_1 = require('express');
var app = express_1["default"]();

这个["default"]从哪里来的?它使我的代码无法正常运行:

var app = express_1["default"]();
                              ^

TypeError: express_1.default is not a function
据我所知,如果我没有使用 “default” 括号,我应该已经获得了代码,并且它可以正常工作 - 我试着删除括号并且它工作了。在这里我错过了什么?

模块应该是 commonjs 而不是 commonsjs。这可能会导致问题。 - unflores
2
考虑使用 esModuleInterop: true 编译器选项(在你的 tsconfig 中设置)。这将允许你像预期的那样使用 import express from 'express' - Biel Simon
你可以尝试使用 import * as express from 'express' 替代 import express from 'express',但这通常取决于 tsconfig.json 中的 esModuleInterop 设置。 - Jindrich Vavruska
5个回答

39

我通过在tsconfig.json中添加以下内容来解决这个问题:

{
  "compilerOptions": {
    ... 
    "module": "commonjs",
    "esModuleInterop": true,
    ...
  }
}

esModuleInterop 标志被描述为:"为运行时Babel生态系统兼容性发出__importStar和__importDefault助手并启用--allowSyntheticDefaultImports以实现类型系统兼容性。"

https://www.typescriptlang.org/docs/handbook/compiler-options.html


1
这解决了我的问题!早知道 ts-node-dev 是使用 TypeScript 的配置,所以我添加了一个 tsconfig.json 文件。谢谢! - Derk Jan Speelman
3
这应该是被接受的答案,因为它允许简单地使用 import express from 'express' - Foxhoundn
我同意这是较新版本的 TypeScript 正确的策略。此选项直到 TypeScript 2.7 才可用,该版本发布于 2018 年。 - dvlsg

37

最安全的解决方案是:

import express = require('express');

这将转译为:

var express = require('express');

导入 require 声明的官方文档可以在此处找到。

我认为 TypeScript 期望一个名为 "default" 的导出作为您上面的代码的功能,根据这里最后一段所述。


顺便提一句:似乎 TypeScript 的最新版本(撰写时的typescript@1.8.0-dev.20151229)将在尝试使用缺失默认值的编译尝试时抛出警告:

index.ts(1,8): error TS1192: Module '"express"' has no default export.

附注2:Microsoft 使用 import * as express from 'express'; 语法的示例可以在这里找到。当针对一个 commonjs 模块(例如在此示例中所示)时,这也会转换为var express = require('express');


如果您至少有 TypeScript 2.7 并且针对 CommonJS,请使用esModuleInterop

链接中提到:

为了给用户提供与 Babel 或 Webpack 相同的运行时行为,TypeScript 在向旧模块格式发出时提供了一个新的 --esModuleInterop 标志。

在新的 --esModuleInterop 标志下,这些可调用的 CommonJS 模块必须像这样作为默认导入进行导入:

import express from "express";

let app = express();

我们强烈建议Node.js用户在使用类似于Express.js这样导出可调用/可构建模块的库时,将模块目标设置为CommonJS,并使用这个标记。


1
"import *" 是导入旧模块的错误方式。请参见 https://dev59.com/lV0b5IYBdhLWcg3wGN_f#29598404。" - C Snover
3
考虑在您的 tsconfig 中设置 esModuleInterop: true 编译选项。这允许您像预期的那样执行 import express from 'express' - Biel Simon

7
如果您想使用像Express.js这样的非ES6模块的默认导出,则需要使用传统的导入语法import express = require('express')
在ES6模块中,没有像Node.js模块的module.exports或AMD模块的return那样的默认值导出;ES6模块的默认导出只是default键。这就是为什么当您尝试使用ES6默认import时,TypeScript会生成访问default属性的JavaScript代码的原因。
有关此更多信息,请参见新的导入commonjs / amd模块的es6语法,即`import foo = require('foo')`

谢谢!我在这个主题上做了一些研究,多亏了你。 - Shikloshi
我不明白为什么你把另一个答案选为正确答案。那是错误的答案。 - C Snover
如果这确实是当前版本的TypeScript的错误答案,那么我建议您让微软编辑他们的示例。 - dvlsg
@dvlsg 那个微软的例子和 OP 所做的不一样!该示例仅使用 expressstatic 属性,尝试使用默认导出(因为它是错误的,无法工作)。在这种情况下,import * 是可以的,因为它使导入的 express 标识符成为一个空列表,然后根据 ES 规范将 express 模块的所有导出属性添加到该列表中。这适用于使用默认导出。 - C Snover
也许是因为我的测试环境和他们的示例都针对commonjs(不管是好是坏或无意中)? 如果目标是未定义模块的ES6,则会导致错误TS1202(不能使用导入分配),但会尝试进行转换。 模块为commonjs的ES6 / ES5都转换为var express = require('express');。而如果针对未定义模块的ES5,则甚至不会尝试进行转换,并建议设置一个模块。 - dvlsg
显示剩余2条评论

5
如果您仍然想要使用import关键字,则可以按照以下方式使用:
import express from "express"; 
// If the above is not supported by your project environment then follow as below
import * as express from "express";

在文件tsconfig.json中。
{
  "compilerOptions": {
    ...   
    "module": "commonjs"
    ...
  }
}

感谢Josh Dando提供的帮助。

1
请确保在您的tsconfig.json文件中设置"module": "commonJS"。 - Josh Dando

-1

另一个可能适合您的解决方案是这样做:

import * as express from express;
const app = express();

它应该以相同的方式工作。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接