Moment: [TypeScript] "error TS2304: Cannot find name 'moment'" when used with "module":"none"

Created on 14 Feb 2017  ·  51Comments  ·  Source: moment/moment

Typescript seems to not be able to resolve the global moment() function when used in a non-module context.

tsc version: 2.1.6
moment version: 2.17.1

Repro (assuming tsc is on your $PATH):

$ mkdir repro && cd repro
$ npm i moment
$ echo 'const now = moment();' > test.ts
$ tsc --module none test.ts
test.ts(1,13): error TS2304: Cannot find name 'moment'.

However, using a module type works as expected:

$ echo 'import * as moment from "moment"; const now = moment();' > test2.ts
$ tsc --module commonjs test2.js

Unfortunately, my project does not use a bundler or module system, so I'm stuck with "module": "none" for now.

Is my only alternative to use something like typings install --save --global dt~moment ?

TypeScript Up-For-Grabs

Most helpful comment

+1 please fix this

All 51 comments

I'm facing the same problem. It doesn't matter what module type you specify in tsconfig.json. I get the same error as @rossipedia

I reported same issue (https://github.com/moment/moment/issues/3663), but unfortunately it was just closed without any reaction.

I ended up copying moment.d.ts into my /scripts/app directory and implemented the fix specified in https://github.com/moment/moment/issues/3663#issuecomment-273199291

Hopefully a more permanent fix will make its way into moment.d.ts eventually.

I solved this by specifying "moduleResolution": "node" in my tsconfig. Not sure if this is an option for you guys, but it seems to do the trick.

"moduleResolution": "node" did not fix it for me, unfortunately

@rossipedia Me either.

Would it not help to add export as namespace moment; into the moment.d.ts file?

See: https://github.com/moment/moment/issues/3808

@czb that also did not fix the issue for me, unfortunately :(

I've resolved this using a couple of hacks that others have mentioned around a ton of issues in lots of projects that are having this problem. Just wrap it up in a custom definition file of your own. This is using Typescript 2.2. This way I'm not mucking with the dependency coming down from npmjs.org and I don't have to check the whole thing into source control.

tsconfig.json

"compilerOptions": {
    "target": "ES5",
    "moduleResolution": "node",
    ...
}

moment.custom.d.ts

import * as _moment from 'moment';
export as namespace moment;
export = _moment;

I'm curious as to why that doesn't work when that code is added directly to moment.d.ts? I can't help but think we're butting into some obscure TypeScript resolution bug, and unfortunately tsc doesn't have a verbose mode (that I know of) that would help pinpoint where things are going wrong.

I think that if you define "module": "none" typescript does not look for d.ts files under the node_modules/moment folder. My experience is that it is only looking under the node_modules/@types folder. So there are basically these options.

  1. Copy moment.d.ts under node_modules/@types/moment
  2. Include /// <reference path="./node_modules/moment/moment.d.ts" /> into your .ts file
  3. Call tsc --typeRoots node_modules test.ts
  4. Put the ./node_modules/moment/moment.d.ts into files section of tsconfig.json

You also need to add export as namespace moment; into the moment.d.ts file.

the Moment team would love any PRs that would fix this, but stay TS 1.x compatible.

I still have yet to find a solution that works at all, but when I do I'll be sure to add a PR

According to #3663 the temporary fix to get it working without any bundler...

  • Copy moment.d.ts from node_modules/moment/ to node_modules/@types/moment

  • Change export = moment; in moment.d.ts to

declare module "moment" {
    export = moment;
}
  • Rename moment.d.ts to index.d.ts

Just use moment and your editor( in my case vscode ) and tsc shouldn't complain

+1 please fix this

@mtgibbs Where are you putting the moment.custom.d.ts file? Is there any other configuration that needs to happen for your app to find this custom.d.ts file?

@elSteeze

Yeah, I'm adding them to my tsconfig.json. Here's a scrubbed example:

{
  "compilerOptions": {
    "target": "ES5",
    "moduleResolution": "node",
    "typeRoots": [
      "./node_modules/@types"
    ]
  },
  "compileOnSave": true,
  "exclude": [
    "node_modules",
    "./path/to/typings/**/*.d.ts",
    "./typings"
  ],
  "include": [
    "./path/to/typings/moment.custom.d.ts",
    "./path/to/src/**/*.ts"
  ]
}

@mtgibbs Hey thanks man! Appreciate the speedy reply. I'll give that a try.

@mtgibbs Sorry for another noob like question, after implementing your solution I started getting editor errors on the moment keyword in my ts code...

'moment' refers to a UMD global, but the current file is a module. Consider adding an import instead

I did some personal research and attempted to implement a solution by rewriting moment.custom.d.ts but unfortunately, that didn't fix my issue.

Sorry, not trying to be one of those pesky dev's who makes everyone else debug their code, I'm a recent grad and I'm new to working with 3rd party libraries in an Angular 2 environment

Here is my updated module.custom.d.ts

declare var moment: any;
declare var module: NodeModule;
interface NodeModule {
    id: string;
}
import * as _moment from 'moment';
export as namespace moment;
export = _moment;

No problem. If you're using Angular 2 you should be on 2.0+ TS. I don't work in Angular so I'm unsure. Mind pasting what you can of your tsconfig.json and I'll take a look after I get back to a real computer.

Sure!

This is my entire tsconfig.es5.json file inside the src directory of my angular 2 module I'm building.
I appreciate the help by the way

{
  "compilerOptions": {
    "declaration": true,
    "module": "es2015",
    "target": "es5",
    "baseUrl": ".",
    "stripInternal": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "moduleResolution": "node",
    "outDir": "../build",
    "rootDir": ".",
    "lib": [
      "es2015",
      "dom"
    ],
    "skipLibCheck": true,
    "typeRoots": [
      "./node_modules/@types"
    ]
  },
  "compileOnSave": true,
  "exclude": [
    "node_modules",
    "./typings/**/*.d.ts",
    "./typings"
  ],
  "include": [
    "./typings/moment.custom.d.ts",
    "./src/**/*.ts"
  ],
  "angularCompilerOptions": {
    "annotateForClosureCompiler": true,
    "strictMetadataEmit": true,
    "skipTemplateCodegen": true,
    "flatModuleOutFile": "new-version-library.js",
    "flatModuleId": "new-version-library"
  },
  "files": [
    "./index.ts"
  ]
}

What version of TS are you compiling in? You should just be able to import moment at the top without my hack.

@mtgibbs I'm using v2.2, and that's what I thought. Unfortunately, it's not working. I'm almost at the point where I write my own version of the functionality we're taking from momentjs, because I can't seem to get this working.

@elSteeze

Ok, going back here. I noticed that you said that you've made a custom module.custom.d.ts, but then you refer to my moment.custom.d.ts in your tsconfig.json. Make sure that you've made moment.custom.d.ts and added it to your compiler, none of that extra stuff that you had declaring it things. Then make sure that you're referencing it at the top of the file you're wanting to use it in.

/// <reference path="path/to/your/moment.custom.d.ts" />

This should allow you to reference it as a global variable like in my original workaround.

Awesome thank you @mtgibbs ! I really do appreciate the help.
I altered my file structure to support your paths. So those are correct paths.
Cheers mate!

This works for me:

  • comment out // export = moment in the official moment.d.ts file
  • add node_modules/moment/moment.d.ts to tsconfig.json

Alternately, copy the modified moment.d.ts to a location of your choice and add the path to it in your tsconfig.json

I think the main problem is that when explicitly using "module":"none" in tsconfig.json you can't have any top level import or exports in any libraries (because you're then using modules).

you have to first import in component as
import * as moment from "moment";

it worked well for me

you have to first import in component as
import * as moment from "moment";
it worked well for me

If you do that in a file, then namespace resolution is disabled for that files.

Pls guys +1 to fix this

Why don't you fix this issue? It is very important.

Adding "module": "commonjs" to the tsconfig.json fixed it for me

But i have to use "module": "none". Commonjs is not a solution.

If I'm understanding it correctly, this will be resolved with TypeScript 2.9's import types feature, which will allow importing type definitions without affecting the the module / ambient context.

I'm using TS 3.0.1, is this now solved? It doesn't work out of the box at least.

EDIT, With TS 3.0.1 I could do just this:

declare var moment: typeof import("moment");

Hurray!

With tsconfig

{
    "compilerOptions": {
        "module": "none"
        "moduleResolution": "node", 
        // ...
    }
}

TS 3.0.1 seems to fix this issue (and it's been open for over a year and a half with no traction)

Hi rossipedia,
I am facing same issue in angular-6 project. As per your comments it's resolved in TS 3.0.1 but angular6 CLI doesn't support TypeScript version 3.0.1.

Can you please help to resolve this issue?

Did anyone else also encounter the @types/moment npm package which is just a stub and tells you to use the file that's provided by the normal moment package?

... Why does moment have to do it differently compared to every other package? This is the reason why this issue exists, and thousands of hours were spent by developers struggling with this.

Sure, @Ciantic's solution works in TS 3.0.1, but I don't have to use something like declare var moment: typeof import("moment"); for any of the other packages I'm using.

It doesn't look like Typescript 3.0.1 fix that issue, it looks like it just give us workaround.

it looks like it just give us workaround.

Yup, it's exactly this. This is not an acceptable "fix".

It's only been a day and I just realized that the TS 3.0.1 workaround only imports the moment function, not the namespace/types. So if you want to pass a moment object into a function, this happens:

image

Please reopen this issue, or better: fix the .d.t.s file.

Not a solution to the original problem, but for people googling this later: specifying the following in tsconfig.json fixed it (typescript 3.3.3 and moment 2.24.0)

{
  "compilerOptions": {
    "module": "commonjs"
  }
}

I'm no longer using moment for much these days, but I'm happy to re-open the issue if it's still valid.

ERROR in src/app/services/get-list.service.ts(22,54): error TS2304: Cannot find name '_'.

what is the solution for this??

@nagipogu No solution for that, this issue is for moment.js, please open your issue in underscore.js's Github repository.

I'm running into this issue with latest version of moment with latest version of TS. Does anyone have any successful workaround?

Still not working, the solution for me was to yarn add -W [email protected].

When I installed the latest moment version and looked into node_modules/moment/package.json, I saw this: https://github.com/moment/moment/blob/develop/package.json#L29 - but there was no existing directory node_modules/moment/ts3.1-typings, so it's not surprising TS coudln't find it. I think there may be some bug with an installation script (or a package) or so. Yes, we have "module": "esnext" in tsconfig.json.

For me it broke with 2.25.0:

I used pnpm add [email protected] as a temporary workaround.

For me it broke with 2.25.0:

I used pnpm add [email protected] as a temporary workaround.

work good, thank you

For me it broke with 2.25.0:

I used pnpm add [email protected] as a temporary workaround.

Same here added it to : "dependencies" {"moment": "^2.24.0",
}...seems to work thank you.

Downgrading to 2.24.0 also worked for me. Looks like a regression in 2.25.0.

Does 2.25.3 fix your issues?

There are so many different types of situations where Moment.js is used, let alone TS 1.x, TS 2.x, TS 3.x...
I am hesitant to make changes that break any group of these users.

If you think the documentation can be improved, please post at https://github.com/moment/momentjs.com/

Does 2.25.3 fix your issues?

Yes.

Rolled back to 2.24.0 when 2.25.1 failed for me in Angular 9. With 2.25.3 it's back on track again.

Ok, great. I will close for now.

Please re-open (or open a new issue) if you still have concerns.

Was this page helpful?
0 / 5 - 0 ratings