How to use momentjs in TypeScript with SystemJS?

2019-02-16 04:56发布

问题:

My project's setup includes 'jspm' tool for libraries and 'tsd' tool for typings.

After installing moment's TypeScript d.ts file (these), I can't find a way to load and actually use a moment instance.

In my file (using SystemJS module loading)

/// <reference path="../../../typings/tsd.d.ts" />
import * as moment from "moment";
import * as _ from "lodash";
...
...
const now = (this.timestamp === 0) ? moment() : moment(this.timestamp);

I get a "TypeError: moment is not a function"

The definitions are structured the same as lodash, which works fine, so I don't know what might be the cause.

Can anyone help?

回答1:

I did the following:

I installed moment definition file as follows:

tsd install moment --save

Then I created main.ts:

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

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

And I ran:

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

main.js looks like this:

// 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());
        }
    }
});

My TypeScript version is 1.6.2.

This is what I found out:

Momentjs exports a function (i.e. _moment = utils_hooks__hooks and utils_hooks__hooks is a function, that's quite clear.

If you place a breakpoint at the place I denoted as PLACE XY above, you can see that moment_1 is an object (!) and not a function. Relevant lines: 1, 2

To conclude it, the problem has nothing to do with TypeScript. The issue is that systemjs does not preserve the information that momentjs exports a function. Systemjs simply copy properties of the exported object from a module (a function is an object in JavaScript too). I guess you should file an issue in systemjs repository to find out if they consider it to be a bug (or a feature :)).



回答2:

Since version 2.13, moment includes Typescript typings. No need to use tsd (or typings) anymore.

In the systemjs.config.js file, just add the following:

  var map = {
    // (...)
    moment: 'node_modules/moment',      
  };

  var packages = {
    // (...)
    moment: { main: 'moment.js', defaultExtension: 'js' },
  };

And in the module:

import moment = require('moment')


回答3:

I had immense trouble getting this to work, as I could not use npm due to proxy restrictions (therefore had to manually install the libraries). Provided the versions of moment and definitelytyped files are installed in appropriate locations in your project, you can get this to work with a bit of fiddling around.

There is a useful note here on the moment.js website on configuring typescript with moment. The key aspect which helped in my case was adding the following in the compilerOptions section of my tsconfig.json file:

"allowSyntheticDefaultImports": true


回答4:

This works with Angular 2 but shouldn't be specific to it. You're basically just telling the System loader where to find moment.min.js.

In system.config.js:

// map tells the System loader where to look for things
var map = {
  'app':                        'app', // 'dist',

  '@angular':                   'node_modules/@angular',
  'rxjs':                       'node_modules/rxjs',

  // tell system where to look for moment
  'moment':                     'node_modules/moment/min'
};

// packages tells the System loader how to load when no filename and/or no extension
var packages = {
  'app':                        { main: 'main.js',  defaultExtension: 'js' },
  'rxjs':                       { defaultExtension: 'js' },

  // tell system which file represents the script when you import
  'moment':                     { main: 'moment.min.js', defaultExtension: 'js'}
};

In your module:

import moment from 'moment';

OR (Edit 10/13/2016):

import * as moment from 'moment';

Some commenters have posted the import * syntax above and I had to switch to that as well after some upgrades (not sure why).



回答5:

In my typescript file, when I use

import moment from 'moment';

it says cannot find module 'moment'. I have installed moment as npm package and it referenced properly but for the reason, that moment.d.ts exports the moment as namespace not module as below, i cannot reference it.

export = moment; 

So if i change it to

declare module 'moment' {
    export default moment;
}

importing works perfectly. What I am doing wrong here?



回答6:

It's a bit not clear if you try to use moment for the client-side development or for the server-side (e.g. node.js). But, if your case is front-end, then I was able to use moment pretty ease:

1) I just added files to my project:

2) Wrote a simple test code:

window.onload = () =>
{
    var timestamp: number = 111111111111111;
    var momentResult: moment.Moment = moment(timestamp);
    alert(momentResult.toISOString());
};

And my project was built OK and I saw a test alert in the browser.



回答7:

Just done it in my project. It's angular, but I don't think that matters and might put you on the right path. It's easy as:

import MomentStatic = moment.MomentStatic;

class HelpdeskTicketController {
  constructor(private moment: MomentStatic) { // angulars dependency injection, you will have some global probably
    let d = this.moment(new Date()); // works
    console.log(d.add(2, 'hours').format()); // works
  }

  ...


回答8:

I assume moment was installed using

jspm install moment

This should add an entry in the map section of your config.js.

...
"moment": "npm:moment@2.10.6",
...

This way you can access moment in your js files via

import moment from 'moment';

console.log(moment().format('dddd, MMMM Do YYYY, h:mm:ss a'));

When you import * as moment you import the module namespace. So moment will be an object which properties are module exports. That way the default export does not get imported as moment but as moment.default. So something like

moment.default().format('dddd, MMMM Do YYYY, h:mm:ss a')

might actually work in you code but is not the way it is intended to be used.



回答9:

For my stuffs, I use Webpack with VS 2015 (Update 3). So after adding codes for the package.json and webpack.config.vendor.js files, I just need to call import moment = require('moment') on the very top of my component (TypeScript, Angular 2).