27

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?

dror
  • 3,759
  • 6
  • 31
  • 45
  • Have you tried `import { moment } from 'moment'`?; It might also be useful to try and use moment 2.8.0 if you can't find a d.ts for the newer version. – Nypan Dec 02 '15 at 19:46
  • 2
    @wilenx that's funny, I just added a bounty to this question about 10 mins ago http://stackoverflow.com/questions/32987273/typescript-module-systems-on-momentjs-behaving-strangely – ColinE Dec 02 '15 at 22:53

9 Answers9

32

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 :)).

MartyIX
  • 27,828
  • 29
  • 136
  • 207
  • The import needs to be like this: `import * as moment from "moment";` – Chriss Mejía Jan 14 '17 at 02:24
  • 1
    Right now instead of `tsd install moment --save` you should use `npm install @types/moment --save` and add `typeRoots` to your `tsconfig.json`: `"typeRoots": [ "../node_modules/@types" ]` – Highmastdon Jan 26 '17 at 11:25
  • @Highmastdon `@types/moment@2.13.0: This is a stub types definition for Moment (https://github.com/moment/moment). Moment provides its own type definitions, so you don't need @types/moment installed!` – Nate Anderson Apr 18 '18 at 23:40
14

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')
Philippe Plantier
  • 7,964
  • 3
  • 27
  • 40
  • A small correction Im using moment 2.15 and i can only import it using import * as moment from 'moment'; If I use the way you mention I get a TS error moment has no default export – Gabriel Guerrero Sep 24 '16 at 22:37
  • Weird. Are you sure you are using the typings distributed with the moment package? What if you use `import moment = require('moment')` ? – Philippe Plantier Sep 25 '16 at 08:03
  • Hey yeah thats using the distributed typings definitions of latest moment, an using typescript 2.0, the import moment = require('moment') seems to work fine, meaning compiles and runs fine, but intellij doesnt autocomplete properly the moment methods as it does using import * as moment from 'moment'; – Gabriel Guerrero Sep 25 '16 at 19:44
  • The problem with `import * as moment from 'moment';` is that methods should run fine, but I don't think things like `new moment()` would work properly. IntelliJ not working with the require syntax is probably a problem with intelliJ (it works with the Atom Typescript module) – Philippe Plantier Sep 26 '16 at 06:59
  • I'll edit my answer because `import moment = require('moment')` is the proper way to do it – Philippe Plantier Sep 26 '16 at 07:01
  • Yes I just saw ts docs, the right way is to use the import moment = require('moment'); you suggested because the use export = at least till they changed it to the new way of doing that with export default sintax – Gabriel Guerrero Sep 26 '16 at 16:55
  • Thanks for the answer, Is there anywhere we can look up and see the list of default supported typings by TypeScript? Actually, it's a bit confusing! I tried to find moment.d.ts and I could not find until I read on GitHub issue about it. – Mohammad Kermani May 04 '17 at 15:30
4

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
RichL
  • 183
  • 1
  • 10
  • This was the only item on this page that worked for me with TypeScript, there's some extra background to the process of getting moment working here: https://github.com/moment/moment/issues/2608 - particularly the comment from gfviegas on the 23rd of May, 2016. After this setting I just had to use import * as moment from 'moment' then moment().format worked as expected. – bwobbones Jan 03 '17 at 15:54
3

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).

Josh Werts
  • 1,431
  • 2
  • 11
  • 12
1

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?

Jojo
  • 47
  • 1
  • 8
  • 1
    Hi Ali, welcome to Stack Overflow! Unfortunately you posted your question as an answer to someone else's question. Instead I recommend that you start a new question of your own and allow the community to help you that way. Good luck! – Vince Horst Oct 18 '16 at 01:48
0

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:

enter image description here

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.

Mark Dolbyrev
  • 1,887
  • 1
  • 17
  • 24
  • My setup includes 'jspm' for libraries and 'tsd' cli for typings, no direct script tags like you used. I'll edit the question to clarify. Thanks for your effort though! – dror Nov 28 '15 at 16:50
0

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
  }

  ...
Jan Peša
  • 6,760
  • 4
  • 27
  • 32
0

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.

brass monkey
  • 5,841
  • 10
  • 36
  • 61
  • Thanks for answering. Writing "import moment from "moment";" causes an error of type "Module 'moment' has no default export" (which is true) – dror Dec 02 '15 at 10:02
  • Which version of momentjs do you use? I think there had been some related discussions. See https://github.com/moment/moment/issues/2576 & https://github.com/moment/moment/pull/2577. – brass monkey Dec 02 '15 at 10:37
  • 2.10.6 and typings for 2.8.0. I find it strange that I can't find a way to integrate it in my typescript, not doing anything special and moment being a library so popular... :( – dror Dec 02 '15 at 10:59
  • That's ok. I appreciate it nevertheless. – dror Dec 02 '15 at 11:12
  • Try: import { moment } from 'moment'; It might also be useful to try and use moment 2.8.0 if you can't find a d.ts for the newer version. – Nypan Dec 02 '15 at 16:52
  • The suggested `import moment from "moment";` seems to work for me with Moment.js 2.13.0, TypeScript 1.8.10, jspm 0.16.33 and system.js 0.19.26. – Ronald Zarīts Apr 30 '16 at 14:59
0

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).

Thomas.Benz
  • 8,381
  • 9
  • 38
  • 65