Cannot redeclare block-scoped variable

ken picture ken · Jul 28, 2016 · Viewed 15.8k times · Source

I have node program which ultimately uses commonjs and therefore my JS files start with a number of require statements. I renamed these JS files to TS with the hope that I could incrementally move into Typescript but am getting these errors:

enter image description here

from the following code:

const RSVP = require('rsvp');
const moment = require('moment');
const firebase = require('universal-firebase');
const email = require('universal-sendgrid');
const sms = require('universal-twilio');
const merge = require('merge');
const typeOf = require('type-of');
const promising = require('promising-help');
const _ = require('lodash');

const up = require('./upload');
const audience = require('./audiences');
const message = require('./messages');

The locally referenced modules like upload, audiences, and messages are likely to define most (all?) of the same modules such lodash, etc. I'm guessing that somehow the namespace scope is not being respected between modules but I'm not sure why.

I'm also unclear whether using ES6 import syntax would properly transpile to a ES5 commonjs "require" module format (this is using Node 0.10.x).

Oh as additional context, my tsconfig.json is:

{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "removeComments": true,
    "sourceMap": true,
    "outDir": "./dist",
    "watch": true
  },
  "compileOnSave": true
}

Note: I've seen other people have gotten the "cannot redeclare block-scoped variable" error before but none of the conversations seemed to really fully fit my situation. Although i'm quite new to Typescript so maybe I'm making a newbie mistake.


Also of note, I noticed some examples of a strange variant of commonjs and ecmascript module formats:

import up = require('./upload');

This is in contrast to how I'd normally write it as:

const up = require('./upload');

When I use the "import" keyword, however, it complains that upload.ts is not a module:

not a module

Answer

Daniel Rosenwasser picture Daniel Rosenwasser · Aug 5, 2016

If we look at the current snapshot of moment.d.ts on DefinitelyTyped, we'll see that it is a global script file (as opposed to a module) with a variable named moment:

declare var moment: moment.MomentStatic;

It's considered a global script file because it doesn't import or export anything.

Right now your file is also a script file. That might not sound correct, but the idea is that TypeScript doesn't recognize require on its own as an import. require could technically be any other function.

Instead, you'll need to use a specific syntax for this:

import moment = require("moment");

All that does is tell TypeScript that this is actually an import, and is safe to transform into an import in CommonJS, AMD, UMD, SystemJS etc.


As a side note, for TypeScript 2.0, we've updated these .d.ts files not to be global script files, and instead be authored as modules. If you see the declaration files for moment in the types-2.0 branch in DefinitelyTyped, you can see the following lines:

declare var moment: moment.MomentStatic;
export = moment;

Where export = moment is module system-agnostic way of writing module.exports = moment.