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

Posted

技术标签:

【中文标题】Node.js + TypeScript:类型脚本编译代码的语法不明确【英文标题】:Node.js + TypeScript: Unclear syntax with type script compiled code 【发布时间】:2016-04-03 21:11:17 【问题描述】:

我正在尝试在我的节点项目中使用 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

据我了解,我应该得到没有“默认”括号的代码,并且它会正常工作 - 我尝试删除括号并且它有效。

我在这里错过了什么?

【问题讨论】:

模块应该是commonjs 而不是commonsjs。这可能会给您带来问题。 考虑使用 esModuleInterop: true 编译器选项(在您的 tsconfig 中设置)。这使您可以按照您的预期执行import express from 'express' 你也可以试试import * as express from 'express',而不是import express from 'express' - 哪个工作通常取决于tsconfig.json中的esModuleInterop设置 【参考方案1】:

最安全的解决方案是:

import express = require('express');

这转换为:

var express = require('express');

import require 声明的官方文档可以在here找到。

从最后一段 here 来看,我相信 TypeScript 期望一个名为“default”的导出作为您上面的代码运行。


旁注:看起来 TypeScript 的最新版本(在撰写本文时为 typescript@1.8.0-dev.20151229)会在尝试使用缺失默认值的编译尝试时抛出警告:

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

旁注 2:可以在 here 找到 Microsoft 使用 import * as express from 'express'; 语法的示例。当针对 commonjs 的模块(在本例中为 they are)时,这也将转换为 var express = require('express');


如果您至少有 TypeScript 2.7 并且以 CommonJS 为目标,您也可以使用 esModuleInterop。

来自链接:

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

在新的 --esModuleInterop 标志下,这些可调用的 CommonJS 模块必须作为默认导入导入,如下所示:

import express from "express";

let app = express();

我们强烈建议 Node.js 用户将此标志与 CommonJS 的模块目标一起用于 Express.js 等库,这些库导出可调用/可构造的模块。

【讨论】:

import * 是导入遗留模块的错误方法。见***.com/a/29598404/252087。 考虑使用 esModuleInterop: true 编译器选项(在您的 tsconfig 中设置)。这使您可以按照您的预期执行import express from 'express'【参考方案2】:

我通过将以下内容添加到 tsconfig.json 解决了这个问题:


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

esModuleInterop 标志被描述为:“发出 __importStar 和 __importDefault 帮助程序以实现运行时 babel 生态系统的兼容性,并启用 --allowSyntheticDefaultImports 以实现类型系统的兼容性。”

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

【讨论】:

这解决了我的问题!可能知道 ts-node-dev 正在使用 TypeScript 的配置,所以我添加了一个 tsconfig.json 文件。谢谢! 这应该是公认的答案,因为它允许简单地使用import express from 'express' 我同意这是较新版本的打字稿的正确策略。直到 2018 年发布的 typescript 2.7 才提供此选项。【参考方案3】:

如果您尝试使用 Express.js 等非 ES6 模块的默认导出,则需要使用旧版导入语法 import express = require('express')

在 ES6 模块中,没有像 Node.js 模块的 module.exports 或 AMD 模块的 return 那样的默认值导出; ES6 模块的默认导出只是 default 键。这就是为什么当您尝试使用 ES6 默认 import 时,TypeScript 会生成 javascript 以访问 default 属性。

更多信息请访问New es6 syntax for importing commonjs / amd modules i.e. `import foo = require('foo')`

【讨论】:

谢谢!多亏了你,我对这个主题进行了一些研究。 我不明白您为什么接受另一个答案作为正确答案。这是错误的答案。 如果当前版本的 Typescript 确实是错误的答案,那么我建议你让微软编辑他们的示例。 @dvlsg Microsoft 的示例与 OP 正在执行的操作相同!该示例仅使用来自expressstatic 属性,它 尝试使用默认导出(因为它不能,因为它是错误的并且不起作用)。在这种情况下,import * 很好,因为它使导入的express 标识符成为一个空白列表,然后根据 ES 规范将 express 模块的所有导出属性添加到该列表中。使用默认导出正确。 据我所知,原始问题中的 const app = express(); 正是 Microsoft example 中所做的事情(当然,减去 const)。我同意require 将在更多情况下工作,但我会相应地进行编辑。【参考方案4】:

如果您仍想使用 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。

【讨论】:

确保你的 tsconfig.json 中有 "module": "commonJS"【参考方案5】:

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

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

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

【讨论】:

以上是关于Node.js + TypeScript:类型脚本编译代码的语法不明确的主要内容,如果未能解决你的问题,请参考以下文章

在 node.js 中扩展 TypeScript 全局对象

nodemon 没有在 webpack-typescript-node.js 中监视目录?

typeScript入门

TypeScript Node.js 包

使用 Typescript 开发 node.js 项目——简单的环境配置

向未来兼容——用ES2015+/TypeScript开发Node.js项目