【问题标题】:Typescript module systems on momentJS behaving strangelymomentJS 上的 Typescript 模块系统行为异常
【发布时间】:2015-10-07 08:24:23
【问题描述】:

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

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

如果我在导入“moment”时使用 "system" 作为打字稿模块系统,我不得不这样做

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

我找到了一种解决方法,无论使用何种 typescript 模块系统,它们都可以正常工作

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

我不喜欢这样,我想了解它为什么会这样。难道我做错了什么?谁能解释一下发生了什么?

【问题讨论】:

  • 从“时刻”导入时刻;似乎可以工作,尽管编译器显示“没有默认导出”错误(但无论如何都会编译)。另外,请参阅此讨论:github.com/Microsoft/TypeScript/issues/…
  • 另外,这篇博文也很有用:jbrantly.com/es6-modules-with-typescript-and-webpack
  • 对我来说 moment.default 不存在任何我导入它的方式,所以我实际上不知道如何创建一个 moment 对象的实例。有人对此有更新的答案吗?
  • 您找到合适的解决方案了吗? ..我对 SystemJS 模块系统有同样的问题...
  • 我仍在使用解决方法:(

标签: typescript momentjs commonjs systemjs


【解决方案1】:

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

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

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

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

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

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

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

【讨论】:

  • 太好了,感谢详细而清晰的解释。非常值得赏金!
  • --allowSyntheticDefaultImports 在 1.8 中实现,现在处于测试阶段
【解决方案2】:

我通过替换解决了这个问题

import moment from "moment";

导入语句

import * as moment from "moment";

这个。

【讨论】:

    【解决方案3】:

    我做了以下事情:

    我安装momentdefinition file如下:

    tsd install moment --save
    

    然后我创建了 ma​​in.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 是一个对象(!)而不是一个函数。相关线路:12

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

    【讨论】:

    • 错误不是在编译时,而是在你尝试使用它的时候。似乎使用 system 会让您找到一个名为 default 的附加字段。如果你使用 commonjs,moment.default 是未定义的,你必须直接使用 moment
    • 谢谢,我尝试调试您的问题,并相应地更新了我的答案。 HTH
    • 它不是复制属性,而是将moment 包装在一个模块对象中。
    • @mk。谢谢你的澄清。
    【解决方案4】:

    这是我使用 System.js 和 Typescript 1.7.5 的方法

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

    但请注意,我使用的是utc() 方法。我不能使用moment(),因为作为 mk。已经解释了,这是由System.js转换成moment.default()的。当然,绝对类型的类型不包含default 方法,因此为了避免编译错误,需要使用moment["default"]() 之类的东西(我知道,丑陋)。

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

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

    在这之后,一切都成了魅力。

    【讨论】:

    • 我只是想说声谢谢,我不使用 SystemJS(我在 tsconfig 中使用 CommonJS 作为 module),但是将 moment 添加到我的 tsconfig 的路径确实修复了我正在使用的库(react-day-picker,它使用moment.js)遇到的问题,非常感谢,因为这个:D FYI;我遇到的错误如下; MomentLocaleUtils.js:68 Uncaught TypeError: (0 , _moment2.default) is not a function
    【解决方案5】:

    加入我正在从事的项目很痛苦,但我们最终用这个解决了它:

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

    【讨论】:

    • 感谢您的回答,但即便如此,使用 systemjs 作为模块系统,这里还是错误消息: Uncaught (in promise) TypeError: moment is not a function。和以前一样
    • 很抱歉没有帮到你...我错过了systemjs部分...我的解决方案是使用commonjs
    • @Brocco 您可以使用const moment: moment.MomentStatic = require("moment");import * as moment from "moment" 来清理您的项目
    【解决方案6】:

    对于我的 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';
    

    【讨论】:

      【解决方案7】:

      我有

      import * as moment from 'moment';

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

      var date: moment = moment();

      var date: moment.Moment = moment();

      【讨论】:

        【解决方案8】:

        如果您使用的是 typescript,则默认导入(如 import moment from "moment")与 const moment = require("moment").default 的行为相同。看 https://www.typescriptlang.org/tsconfig#esModuleInterop

        TLDR; 在 tsconfig.json 中添加 esModuleInterop: true

        【讨论】:

          猜你喜欢
          • 2018-05-06
          • 2012-03-30
          • 1970-01-01
          • 2021-04-23
          • 1970-01-01
          • 1970-01-01
          • 2021-12-11
          • 2020-02-01
          • 2017-08-04
          相关资源
          最近更新 更多