I'm trying to use momentJs from typescript: depending on what module system I'm using to compile typescript, I find a different behaviour on how I can use momentJs. When compiling typescript with commonJs everything works as expected and I can just follow momentJs documentation:
import moment = require("moment");
moment(new Date()); //this works
If I use "system" as typescript module system when I import "moment" I am forced to do
import moment = require("moment");
moment.default(new Date()); //this works
moment(new Date()); //this doesn't work
I found a workaround to make them both work regardless of typescript module system used
import m = require("moment")
var moment : moment.MomentStatic;
moment = (m as any).default || m;
I don't like this, and I would like to understand why it behaves like this. Am I doing something wrong? Can anybody explain me what's happening?
Moment was a pain to pull into the project that I'm working on, but we ended up solving it using this:
For my system.config:
Importing momentjs in my component I removed the * which I think treats the code in the moment.js file as multiple objects.
Change:
to:
Here is how I did with System.js and Typescript 1.7.5
But note that I am using
utc()
method. I cannot usemoment()
because as mk. has explained, this is converted intomoment.default()
by System.js. Of cause Definitely Typed typings do not containdefault
method, so to avoid compilation error one would need to use something likemoment["default"]()
(I know, ugly).Next step, I needed to add the following to System.js config:
After this, all worked as a charm.
This is happening because SystemJS is automatically converting
moment
to an ES6-style module by wrapping it in a module object, while CommonJS is not.When CommonJS pulls in
moment
, we get the actualmoment
function. This is what we've been doing in JavaScript for a while now, and it should look very familiar. It's as if you wrote:When SystemJS pulls in
moment
, it doesn't give you the moment function. It creates an object with the moment function assigned to a property nameddefault
. It's as if you wrote:Why does it do that? Because a module should be a map of one or more properties, not a function, according to ES6/TS. In ES6, the convention for massive external libraries that formerly exported themselves is to export themselves under the
default
property of a module object usingexport default
(read more here; in ES6/TypeScript, you can import functions like this using the compactimport moment from "moment"
syntax).You're not doing anything wrong, you just need to pick the format of your imported modules, and stick to your choice. If you want to use both CommonJS and SystemJS, you might look into configuring them to use the same import style. A search for 'systemjs default import' led me to this discussion of your issue, in which they implement the
--allowSyntheticDefaultImports
setting.I did the following:
I installed
moment
definition file as follows:Then I created main.ts:
And I ran:
main.js
looks like this:My TypeScript version is 1.6.2.
This is what I found out:
Momentjs exports a function (i.e.
_moment = utils_hooks__hooks
andutils_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 thatmoment_1
is an object (!) and not a function. Relevant lines: 1, 2TL;DR: 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 :)).
I had
import * as moment from 'moment';
and changed everything that I thought should be
var date: moment = moment();
to
var date: moment.Moment = moment();