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

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 ?

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 21
  • Comments: 53 (3 by maintainers)

Most upvoted comments

+1 please fix this

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

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

it worked well for me

For me it broke with 2.25.0:

I used pnpm add moment@2.24.0 as a temporary workaround.

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

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", 
        // ...
    }
}

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

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

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

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.

@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"
  ]
}

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

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

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.

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

@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;

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

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

Still not working, the solution for me was to yarn add -W moment@2.22.1.

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.

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

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.

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

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.

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?

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.

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