TypeScript: "Cannot compile namespaces" error with --isolatedModules and no namespaces

TypeScript Version: nightly (2.3.0-dev.20170417)

Code

function f() {}

Expected behavior:

No error, or an error message about a non-module file in an --isolatedModules project.

Actual behavior:

src/a.ts(1,1): error TS1208: Cannot compile namespaces when the '--isolatedModules' flag is provided.

This occurs because we in program.ts verifyCompilerOptions to look for any source file that isn’t an external module declaration, and fail on the first one, whatever it may be. The error message should be upgraded to reflect the real reason we issue this error, and not mention namespaces. Alternately, we could just allow files without imports and actually look for a namespace before adding this error.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 30
  • Comments: 54 (4 by maintainers)

Commits related to this issue

Most upvoted comments

A global file cannot be compiled using '--isolatedModules'. Ensure your file contains imports, exports, or an 'export {}' statement.

ATTENTION - If you are using a create-react-app and you see this issue like I am… I was able to bypass this by making a change to my tsconfig.json file…

Changing the following

    "isolatedModules": true,

to

    "isolatedModules": false,

Just add image

I wound up here while trying to fix a sort-of-related annoyance. Comments below are just to help any other VS Code users searching about this error with create-react-app and TypeScript.

When configuring a proxy with create-react-app 2.x and TypeScript, you add a file named setupProxy.js with code like this:

const proxy = require('http-proxy-middleware');
module.exports = function(app) { /* ... */ };

react-scripts reads this file outside of the ts transpilation process, so it must be plain js.

This works exactly as documented, but it has an annoying side effect.

The tsconfig.json generated by react-scripts includes all files in src, including setupProxy.js. As a result, it triggers VS Code to show the Cannot compile namespaces... error in setupProxy.js, mark the code with a red squiggle and include it in the Problems pane. In my setup, it also highlights the file and all its ancestor folders in red.

Solution: Suppress the VS Code error message by excluding the setupProxy.js file in tsconfig.json.

{
  // ...
  "exclude": ["src/setupProxy.js"]
}

react-scripts forces isolatedModules: true, but it doesn’t appear to overwrite exclude.

Alternative: Add a named export to `setupProxy.js:

export const _ = '';

With a top-level export, it’s now valid with --isolatedModules.

I often find myself doing this in small tests.

describe('jest', () => {
  it('finds this test', () => {
    expect(true).toBeTruthy();
  });

  it('parses this typescript', () => {
    const actual: number = 0;
    const expected: number = 0;
    expect(actual).toEqual(expected);
  });
});

export {
  // Use an empty export to please Babel's single file emit.
  // https://github.com/Microsoft/TypeScript/issues/15230
}

I really want an option that causes TypeScript to consider every .ts file as an ES6 module, regardless of whether it contains import/export statements or not. I originally assumed the --isolatedModules option was that option, but it causes this extremely confusing error on import/export-less files. (I still don’t understand what the error message means by namespaces. I don’t think I’m using that feature.) The reason I want this setting is because I’ve had several issues go uncaught by TypeScript because I accidentally referenced a top-level variable from one .ts file in another (which both didn’t have import/export statements, so TypeScript didn’t treat them as ES6 modules) despite using node/parcel/webpack where files can’t reference each other’s unexported variables like that.

@jamespfarrell Put export {}; somewhere in your file.

Maybe somebody else will have the same issue: I had the same error appearing for a few files I’ve left as placeholders with no content in them but I’ve been importing them in other files, ie. I had empty styles.js file which was imported in another component file.

Here’s a minimal repro case, using typescript@next (full repo at https://github.com/yang/sandbox-ts-namespaces-error):

src/a.js: (notice, no import/export keywords needed)

module.exports = "hello"; // you could really put anything here, e.g. console.log('hello');

tsconfig.js:

{
  "compilerOptions": {
    "allowJs": true,
    "isolatedModules": true,
    "noEmit": true,
    "strict": true
  },
  "include": ["src"]
}

Error:

$ ./node_modules/.bin/tsc
src/a.js:1:1 - error TS1208: Cannot compile namespaces when the '--isolatedModules' flag is provided.

1 module.exports = "hello";
  ~~~~~~


Found 1 error.

I too first encountered this via create-react-app --typescript, which instructs you to create a setupProxy.js (or setupTests.js). CRA also sets isolatedModules and allowJs to true. It seems to have not been an issue until a more recent typescript version (I didn’t see errors about setupProxy.js while on typescript 3.1.x, only after updating to 3.2.x or 3.3.x).

The workaround of adding .js files to your exclude set works when running tsc, but - annoyingly - you still see errors from those files in editors from the TS language service (tried both VS Code and Webstorm). (This seems to be a separate and more general issue with the TS language service not respecting excludes - but I wasn’t able to find an existing issue for that.) To mask this error in the editors, I added .d.ts files for the *.js files.

Furthermore, require-ing or import-ing the .js files renders the exclude ineffective as well, causing the error to be raised (outside your editor).

Only disabling allowJs (against what create-react-app suggests) seems to be working for me.

No idea why i’m getting this error on that specific file. Other files behave fine

screen shot 2019-02-18 at 12 49 07 screen shot 2019-02-18 at 12 48 34

Yes, that will work around the issue. (Assuming you’re not actually using TypeScript namespace. Otherwise you have a different and probably legitimate issue.)

Just figured it out if anyone else is having the same issue. Simply add your declaration types to the react-app-env.d.ts file in the source folder when using create react app.

So I had this error because I had a .js file in the root folder of my project, in my tsconfig.json file I had this configured:

"compilerOptions": {
    ....
  },
  "include": ["src"],

I had to switch it for this:

"compilerOptions": {
    ....
  },
  "include": ["src/*"],

That fixed it, but I don’t understand why, can someone explain it to me? My understanding was that including src would eliminate root files and sibling folders.

So the solution should be: detect real namespace usage before throwing out this error message (i.e the “alternative” solution that @andy-ms proposed). We can’t just change the text to something else because an error message in this case is totally wrong. I want to make a PR, but can I still be trusted here after spewing out all those bullshit?

I had this error for a module definition that I accidentally suffixed .ts instead of .d.ts, if anyone else is making that particular mistake. Have to restart server after fixing.

Those errors you’re getting now are unrelated to the isolatedModules setting. If you turned off isolatedModules, you’ll still get those errors.

Attempted import error: ‘./workers/HeartBeat.worker.js’ does not contain a default export (imported as ‘HeartBeatWorker’).

Where is this error happening? It sounds like you’re trying to import the default export of a file that doesn’t have one, like import Foo from './workers/HeartBeat.worker.js';. If you don’t want to import a default export, then change your import line to import './workers/HeartBeat.worker.js';.

@Macil thanks for your advice.

I get this error:

Attempted import error: ‘./workers/HeartBeat.worker.js’ does not contain a default export (imported as ‘HeartBeatWorker’).

If I add:

export default {};

I get:

Uncaught TypeError: workers_HeartBeat_worker_js__WEBPACK_IMPORTED_MODULE_2_.default is not a constructor

@Bnaya at a guess, the files that work are using import or export statements? Only use one of require()/module.exports and import/export in your code. Otherwise, check that all your files are covered by the tsconfig include (check the docs for full detail on that

@TomasHubelbauer Module declarations should be in a .d.ts for this reason, amongst others. I only had CRA re-add settings I removed, not change them back, but perhaps it’s more insistent for isolatedModules?

I am in a situation where I am using CRA TypeScript and importing a package which has no @types. I want to add a declare module 'package' to provide my own types, but I cannot put this statement in any module file. So I go ahead and create types.ts to place the declare module statement there. Now I get this error.

When I override isolatedModules to false, CRA changes it right back for me. That’s not a solution.

When I add export {} to the types.ts file with declare module, I get Invalid module name in augmentation. instead. This error is avoided by putting the declaration in a non-module file, but CRA through this forced isolatedModules setting forces every file to be a module!

So there is a circular path of solving one error only to land on another and back. How can this be resolved?

Just ran into this message due to a file an “empty” file (i.e. the whole file was commented out). I’m happy TS caught this, but the error message was extremely opaque (and the colors were broken on a vanilla CentOS 7 Install, but this probably due to the webpack dev server, not related to TS directly)

screen shot 2019-02-06 at 14 20 14

With --isolatedModules on, would it compile to garbage code and confuse the user? No it won’t

Yes, it will.

The point of the --isolatedModules flag is to validate that a program can be successfully compiled through single-file transpilation.

The TypeScript codebase can’t be successfully compiled through single-file transpilation. There is code like this everywhere:

file1

namespace ts {
  export var x = 10;
}

file2

namespace ts {
  var y = x; // transpiles to ts.x during whole-program compilation
}

If we single-file transpiled file2, it would break.

@mhegazy Yes and that rule is exactly the root of the problem we have here. The rule unfairly treats a file without import or export as a part of the legacy internal module system, so --isolatedModules rejects the file.
Now we should not change the rule because that will probably break a lot of things, but we can solve the current issue very easily just by removing this error message.
Let’s imagine this “cannot compile namespaces” error message being removed, what could go wrong? What about a codebase that uses internal module, like the Typescript compiler itself? With --isolatedModules on, would it compile to garbage code and confuse the user? No it won’t, there would be a lot of Typescript error messages about undefined symbols, because --isolatedModules broke the links between the files, pretty obvious to understand. Can you find ANY downsides with removing this error message? I couldn’t. Why wouldn’t we choose the easiest solution, that involves zero work (just remove the lines) and has no downsides?

@andy-ms I was trying to point out that the whole code to check for this error is just pointless, and we should just remove it instead of complicating the code more. My point is, when --isolatedModules is on, every file should be regarded as an external (ES6) module, period. In that sense, usage of Namespaces should be legal because right now, it is perfectly legal to use Namespaces inside ES6 modules (with --isolatedModules on).

In #15839 I intended to modify the checking behavior but later realized that this check is just pointless and the ideal solution is to remove it. Typescript has always allowed usage of namespace inside an external module with --isolatedModules on:

export function something() {} // export found, so this module is considered 'external'
namespace ns {} // namespace is still valid in an external module

Thus the check isn’t doing anything in that (seemingly invalid) case, when it’s supposed to complain. So why complain when the programmer isn’t even using any namespace, doing nothing wrong?

There should not be an error message in this situation. The --isolatedModules option should just turn off the internal module system completely, if tsc still errors out this way then the option didn’t do its job.