How to consume the Moment.js TypeScript definition file if my site is already using moment.min.js?
Asked Answered
C

5

19

I'm in the process of converting a website to use TypeScript and I'm converting just one of many JavaScript files to TypeScript. All pages of my site already reference moment.js, such as:

<script src="/scripts/moment.min.js"></script>

I've added other TypeScript definition files using:

npm install --save-dev @types/jquery

... But, the above seems like the wrong choice for moment. When I use the above command (but substitute 'moment' for 'jquery'), a readme file is downloaded that says:

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!

As a solution, I tried saving the moment.d.ts file from its GitHub repo and referencing it in the TypeScript file as so:

///<reference path="../../Scripts/typeDeclarations/moment.d.ts"/>
var now:any = moment.utc();

But, TypeScript gives me the warning:

Cannot find the name 'moment'

Carlisle answered 14/2, 2017 at 18:56 Comment(3)
Did you follow moment typescript guide? Does it cover your usecase?Affiche
I'm not sure if it covers my use case. I tried. The result is that a require statement is added to the generated JavaScript. But, I'm trying to avoid a require statement if possible.Carlisle
@Affiche as of 2018-06-20 the moment typescript guide does not help, nor does their moment.d.ts file - problems arise if you are not using a module loader, ts converts import statements into something like Object.defineProperty(exports, "__esModule", { value: !0 }); var moment = require("moment"); what did work for me was Bruno's ans below i.e. commenting out //export = moment; from their d.ts file.Spiller
M
10

One way to do this is create a custom typing, e.g. custom-typings/moment.d.ts:

export * from 'moment'
export as namespace moment

And include that folder in your tsconfig.json:

{
  include: [
    "custom-typings"
  ]
}
Moneyer answered 19/2, 2017 at 0:40 Comment(8)
I like this solution as it does not involve editing up-stream files.Xerophilous
For some reason, after adding the "include" property to my tsconfig.json file, VS stops compiling my files on build. It does when saving a .ts file, but not when building the project, and that gives me problems when publishing. Went with @dtabuenc solution.Latinize
Do you have multiple tsconfig files? As you say it does when saving a .ts file, that means it is working. Maybe you just didn't include the custom-typings in your build config.Moneyer
I'm getting TS2498: ...uses 'export =' and cannot be used with 'export *'Lackaday
@Lackaday see if you have esModuleInterop enabled.Moneyer
it still don't work, but I found an alternative that works and is similar to yours: github.com/moment/moment/issues/3808#issuecomment-406029986 import * as moment from "moment"; export as namespace moment; export = moment;Lackaday
Do you have a repro? I would like to take a look into why you have the problem. What you did was exporting the module namespace object as TypeScript namespace. There might be come corner cases it does not cover.Moneyer
exactly what I am looking for.Abdicate
N
14

TL;DR;

Add the moment.d.ts (link) in your project from the moment github site and comment out the last line

 //export = moment;

I have created a mini test project to prove this and this works for me.

Project structure

--
  |
  -typings/
     -moment/
       -moment.d.ts   <-- //export = moment; commented out
  -typescript/
     -main.ts
  -tsconfig.json

Contents of both typings and typescript are part of the compilation target, i.e. not excluded in tsconfig.json which looks like this

{
    "compilerOptions": {
        "target": "es5",
        "declaration": false,
        "noImplicitAny": true,
        "removeComments": true,
        "outDir": "dist",
        "jsx": "react",
        "sourceMap": true,
        "experimentalDecorators": true
    },
    "compileOnSave": true,
    "exclude": [
        "node_modules",
        "dist"
    ]
}

The main.ts file contains

const now: moment.Moment = moment.utc();

and compiles... As expected, my IDE gives me auto-completion from the definition file. (no need for ///<reference tags - you should avoid them anyway)

Removing the export statement at the end of the definition file, "transforms" it into an internal - but global - module declaration file.

Nomination answered 17/2, 2017 at 6:14 Comment(0)
M
10

One way to do this is create a custom typing, e.g. custom-typings/moment.d.ts:

export * from 'moment'
export as namespace moment

And include that folder in your tsconfig.json:

{
  include: [
    "custom-typings"
  ]
}
Moneyer answered 19/2, 2017 at 0:40 Comment(8)
I like this solution as it does not involve editing up-stream files.Xerophilous
For some reason, after adding the "include" property to my tsconfig.json file, VS stops compiling my files on build. It does when saving a .ts file, but not when building the project, and that gives me problems when publishing. Went with @dtabuenc solution.Latinize
Do you have multiple tsconfig files? As you say it does when saving a .ts file, that means it is working. Maybe you just didn't include the custom-typings in your build config.Moneyer
I'm getting TS2498: ...uses 'export =' and cannot be used with 'export *'Lackaday
@Lackaday see if you have esModuleInterop enabled.Moneyer
it still don't work, but I found an alternative that works and is similar to yours: github.com/moment/moment/issues/3808#issuecomment-406029986 import * as moment from "moment"; export as namespace moment; export = moment;Lackaday
Do you have a repro? I would like to take a look into why you have the problem. What you did was exporting the module namespace object as TypeScript namespace. There might be come corner cases it does not cover.Moneyer
exactly what I am looking for.Abdicate
X
7

Unfortunately, the momentjs type declarations are written in a purely modular syntax. This is because these days, many people are using npm to acquire js packages and then using module loaders such as Webpack or Browserify to bundle the npm modules in a format usable for the browser.

In your case, you are not using modules, but just relying on importing browser scripts in a global context. In order to support this, the authors of the definition files should have made the definition following UMD guidelines,which is a way to write the definition so that they support both modular and global ambient use.

Fortunately, it's extremely easy for you to add this to your copy of the definitions. Simply add the following line to the top of moment.d.ts

export as namespace moment;

What this does is basically say that when you are referencing moment.d.ts with a tripple-slash directive, it will make available everything exported from the module under a global namespace with the name of 'moment'.

This means there should now be a global moment variable accessible in the global context and you should no longer see the Cannot find the name 'moment' error.

This should be all you need to do.

Xerophilous answered 17/2, 2017 at 3:39 Comment(2)
This worked for me. Moment has a PR open to address this issue.Eighteen
I would also look at @Moneyer 's answer as it is a better/less intrusive way of achieving the same thing.Xerophilous
I
3

As of version 2.13.0, Moment includes a typescript definition file. So if you are using this version you can do following:

import * as moment from 'moment';


let now = moment().format('LLLL');

Note: If you have trouble importing moment, try add "allowSyntheticDefaultImports": true in compilerOptions in your tsconfig.json file.

For reference please visit official doc.

Infirmity answered 22/2, 2017 at 12:33 Comment(2)
This is a mere copy of the documentation, that I suspect the OP followed, and assumes that the OP is using ES6 or CommonJS.Nomination
Yeah I found it on documentation only.Infirmity
I
0

@Hitesh Kumar
Note: If you have trouble importing moment, try add "allowSyntheticDefaultImports": true in compilerOptions in your tsconfig.json file.

👍 work this as version 2.29.4 but the import must be

import moment from 'moment';
Inarch answered 26/5, 2023 at 16:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.