4

I am building some typescript/react console app for Node.js.

If there is any import module in the source code it adds the export {}; into the output.

How I can get rid of the stuff please?

I use

  • typescript 4.1.2
  • ts-node
  • tsconfig:
{
    "compilerOptions": {
        "module": "ESNext",
        "jsx": "react",
        "esModuleInterop": true,
        "moduleResolution": "node",
        "skipLibCheck": true,
    },
    "include": [
        "src/**/*"
    ],
    "exclude": [
        "node_modules",
    ]
}

Source code:

import { useState, useEffect } from "react";
console.log("aaa");

Output:

console.log("aaa");
export {}; // <------ the problem

Fun fact: when I remove the import, the export disappear.

1
  • I believe this is because any file that includes an import or export is considered a module not just a script (in both ES2015+ and TS). And once transpiled, it stays a module, so the export {}; is added to ensure it's still a module, not a script, in JavaScript's eyes. I may be wrong on the latter part though. Commented Jan 3, 2021 at 18:52

3 Answers 3

4

With the introduction of modules in ECMAScript 2015, ECMAScript has been split into two slightly incompatible languages: Scripts and Modules. However, there is no way to explicitly mark a resource as either a Script or a Module in-band. The only way to do that is out-of-band, e.g. via HTTP Content-Types or a command line flag that explicitly tells the ECMAScript engine to interpret the file as a Module.

So, in order to make it clear to the ECMAScript execution engine, that this is, in fact, a Module not a Script, the only way is to make sure that the resource can only legally interpreted as a Module and not a Script.

An empty export statement serves that purpose, since it is illegal in Scripts but has no side-effects.

Long story short: you cannot remove the export statement, because that will make the file ambiguous: it is impossible to tell from the source code alone whether the file is a Script or a Module. And for reasons of backwards-compatibility, most ECMAScript engines interpret ambiguous resources as Scripts. (A more strict engine might reject the file altogether, which is also not what you want.)

So, if you remove the export statement, your file will no longer be interpreted as a Module. However, the TypeScript source file is a Module (because it contains an import statement), therefore the compiler must emit the export statement in order to ensure that the compiled file is also a Module.

Sign up to request clarification or add additional context in comments.

3 Comments

How can we make it compile to a script?
@nog642: By making the source file a Script. Scripts get compiled to Scripts, Modules get compiled to Modules. As soon as your code contains an import or export, it is a Module, since those are illegal in Scripts.
What about import type statements? How would we get typing for e.g. jQuery if using typescript to write a script?
2

From this issue comment, if you are importing it just for typing, use type declaration instead:

type Foo = import("./Foo.js").Foo;

const foo: Foo = {
    fooValue: 0,
};

I found this out while making a Chrome Extension with a Content Script where it cannot be a module but TypeScript keeps adding empty export {} statement to the end of the file.

Comments

2

I’ve been pulling my hair with this myself and have found a workable solution. The trick is to use flag --isolatedModules during build.

As an example say that you in your TypeScript project have a main.ts file in your static folder to serve the client (browser) with functionality; src/static/scripts/main.ts.

This is what main.ts file looks like:

const logger = () => {
  console.log('Hello browser console!');
};

Having property “type”: “module” set in package.json and building (compiling) your project with tsc will generate the following file dist/static/scripts/main.js:

const logger = () => {
    console.log('Hello browser console!');
};
export {};

This won’t work in browser. Building without property “type”: “module” set in package.json will generate the following main.js file:

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const logger = () => {
    console.log('Hello browser console!');
};

That doesn't work either in the browser. The trick is to build the main.ts file by itself using the --isolatedModules flag. The rest of the files can be built in normal way.

In package.json make the following build script:

"build": "tsc && tsc --isolatedModules ./src/static/scripts/main.ts --outDir dist/static/scripts"

In tsconfig.json exclude main.ts file from ordinary build:

"exclude": ["node_modules", "dist", "src/static/scripts/main.ts"]

Then run your build script and you will receive this main.js file:

var logger = function () {
    console.log(‘Hello browser console!');
};

And voila – it works like a charm in the browser. Hope it helps.

8 Comments

"this won't work in the browser" citation needed. Empty exports should be perfectly fine as long as you set type="module" in your script tag. You are talking about transpiling for non-module scenarios, and this is not the correct way to achieve that, you should be using an appropriate bundler (webpack, rollup, parcel, vite, esbuild, lots of options here) if you need that functionality.
Ok. Since the task was to get rid of export {} I just showed a simple workable solution so that the script (main.js) would be recognized and could be used in the script tags in a html page. It works for my needs at least. Why can’t I use this “workaround” approach according to you? I’m eager to learn more.
Because a) it's a hack b) it's possibly a bug in the TS compiler since according to the docs the --isolatedModules flag is not supposed to affect the emitted code and c) it's completely unnecessary: having the empty export is a feature, not a bug, and (again) it works fine in a browser as long as you set type="module" on the script tag in the HTML. The problem also goes away if you do any kind of bundling, there are other good reasons to use one, and there are plenty of them to choose from.
Awesome, it did the job! Thanks, really appreciate your input and feedback. No more hacks that (luckily) works sometimes :)
Yepp that is what I figured. Thanks for your help. I have a better understanding about this now.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.