momentJS 上的 Typescript 模块系统表现异常

Typescript module systems on momentJS behaving strangely

我正在尝试使用打字稿中的 momentJs: 根据我用来编译打字稿的模块系统,我发现了关于如何使用 momentJs 的不同行为。 当使用 commonJs 编译打字稿时,一切都按预期工作,我可以按照 momentJs 文档进行操作:

import moment = require("moment");
moment(new Date()); //this works

如果我在导入时使用 "system" 作为打字稿模块系统 "moment" 我被迫做

import moment = require("moment");
moment.default(new Date()); //this works
moment(new Date()); //this doesn't work

我找到了一种解决方法,无论使用何种打字稿模块系统,它们都能正常工作

import m = require("moment")
var moment : moment.MomentStatic;
moment = (m as any).default || m;

我不喜欢这样,我想了解为什么它会这样。难道我做错了什么?谁能给我解释一下这是怎么回事?

进入我正在从事的项目的那一刻是痛苦的,但我们最终用这个解决了它:

import momentRef = require('moment');
var moment: moment.MomentStatic = momentRef;

我做了以下事情:

我安装了momentdefinition file如下:

tsd install moment --save

然后我创建了main.ts:

///<reference path="typings/moment/moment.d.ts" />

import moment = require("moment");
moment(new Date());

而我运行:

$ tsc --module system --target es5 main.ts # no error 
$ tsc --module commonjs --target es5 main.ts # no error 

main.js 看起来像这样:

// https://github.com/ModuleLoader/es6-module-loader/blob/v0.17.0/docs/system-register.md - this is the corresponding doc
///<reference path="typings/moment/moment.d.ts" />
System.register(["moment"], function(exports_1) {
    var moment;
    return {
        setters:[
            function (moment_1) {
                // You can place `debugger;` command to debug the issue
                // "PLACE XY"
                moment = moment_1;
            }],
        execute: function() {
            moment(new Date());
        }
    }
});

我的 TypeScript 版本是 1.6.2。

这是我发现的:

Momentjs 导出一个function(即_moment = utils_hooks__hooksutils_hooks__hooks是一个函数,很清楚。

如果你在我上面标为PLACE XY的地方下一个断点,你可以看到moment_1是一个对象(!)而不是一个函数。相关行:1, 2

TL;DR: 综上所述,问题与TypeScript无关。问题是 systemjs 不保留 momentjs 导出函数的信息。 Systemjs 只是从模块中复制导出对象的属性(函数也是 JavaScript 中的对象)。我想你应该在 systemjs 存储库中提交一个问题,看看他们是否认为它是一个错误(或一个功能 :))。

发生这种情况是因为 SystemJS 通过将其包装在模块对象中而自动将 moment 转换为 ES6 样式的模块,而 CommonJS 则不会。

CommonJS 引入 moment 时,我们得到了实际的 moment 函数。这是我们在 JavaScript 中已经做了一段时间的事情,看起来应该很熟悉。就好像你写了:

var moment = function moment() {/*implementation*/}

SystemJS 引入 moment 时,它不会给你 moment 函数。它创建一个对象,其矩函数分配给一个名为 default 的 属性。就好像你写了:

var moment = {
    default: function moment() {/*implementation*/}
}

为什么要这样做?因为根据 ES6/TS,模块应该是一个或多个属性的映射,而不是函数。在 ES6 中,以前导出自身的大量外部库的约定是使用 export default 在模块对象的 default 属性 下导出自身(阅读更多 here;在 ES6/TypeScript,您可以使用紧凑的 import moment from "moment" 语法导入这样的函数。

您没有做错任何事,您只需要选择导入模块的格式,并坚持您的选择。如果你想同时使用 CommonJS 和 SystemJS,你可能会考虑将它们配置为使用相同的导入样式。搜索 'systemjs default import' 让我找到了你的问题的 this discussion,他们在其中实施了 --allowSyntheticDefaultImports 设置。

这是我使用 System.js 和 Typescript 1.7.5

的方式
import * as moment from "moment";
...
moment.utc(); // outputs 2015 (for now).

但请注意,我使用的是 utc() 方法。我不能使用 moment() 因为作为 mk。已经说明了,这个由System.js转换成moment.default()。因为 Definitely Typed 类型不包含 default 方法,所以为了避免编译错误,需要使用类似 moment["default"]() 的东西(我知道,丑陋)。

下一步,我需要将以下内容添加到 System.js 配置中:

System.config({
  paths: {
    'moment': 'node_modules/moment/moment.js'
  }
});

此后,一切如常。

我的 system.config:

paths: {
    'moment': 'node_modules/moment/moment.js'
},
packages: {
    app: {
        format: 'register',
        defaultExtension: 'js'
    }
}

在我的组件中导入 momentjs 我删除了我认为将 moment.js 文件中的代码视为多个对象的 *。

变化:

import * as moment from 'moment';

至:

import moment from 'moment';

我有

import * as moment from 'moment';

并改变了我认为应该的一切

var date: moment = moment();

var date: moment.Moment = moment();

我通过替换

解决了这个问题
import moment from "moment";

导入语句

import * as moment from "moment";

这个。

如果您使用的是打字稿,则像 import moment from "moment" 这样的默认导入与 const moment = require("moment").default 的作用相同。看 https://www.typescriptlang.org/tsconfig#esModuleInterop

TLDR; 在您的 tsconfig.json

中添加 esModuleInterop: true