Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider emitting export {} in all ambiguous module output #38696

Closed
DanielRosenwasser opened this issue May 20, 2020 · 10 comments · Fixed by #38712
Closed

Consider emitting export {} in all ambiguous module output #38696

DanielRosenwasser opened this issue May 20, 2020 · 10 comments · Fixed by #38712
Assignees
Labels
Committed The team has roadmapped this issue Suggestion An idea for TypeScript

Comments

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented May 20, 2020

In TypeScript 3.9, we started preserving export * for type-only modules. While this keeps our behavior more predictable long-term, this caused bundle-size regressions for teams combining Webpack and TypeScript (webpack/webpack#10889).

My understanding of the situation is that tools like Webpack use a similar disambiguation strategy as that of TypeScript to figure out whether a file is a module. Given that, when Webpack resolved to an empty file, it would bail out of certain optimizations thinking it was a global script file. In reality, all users using ts-loader's transpileModule mode were hitting this as well - it just turned out that the new behavior uncovered this behavior in a very large codebase.

We also discovered that we were not emitting "use strict" prologue directives even under alwaysStrict because we assumed that the output would be a module.

I also just discovered that import type { foo } from "bar" doesn't force an export {} in a module that does have values in it. That seems bad because this goes beyond special-casing for empty modules.

Here's the outcomes I could imagine:

  • TypeScript always emits an export {}. Is this really useful if the fix is already in-flight for Webpack?
  • TypeScript always emits an export {} under alwaysStrict when the output is empty (but then why does this matter?)
  • TypeScript keeps the current behavior.

Personally, my gut was to always emit the export {}; then I swung into thinking that emitting export {} on empty modules would be a weird vestigial behavior for empty modules. But then I realized that TypeScript doesn't emit any sort of module marker even when using import type, so I'm back to thinking we should always emit export {}.

@DanielRosenwasser DanielRosenwasser added Suggestion An idea for TypeScript Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. In Discussion Not yet reached consensus labels May 20, 2020
@DanielRosenwasser DanielRosenwasser added Committed The team has roadmapped this issue and removed In Discussion Not yet reached consensus Needs Proposal This issue needs a plan that clarifies the finer details of how it could be implemented. labels May 21, 2020
@DanielRosenwasser DanielRosenwasser added this to the TypeScript 4.0 milestone May 21, 2020
@WORMSS
Copy link

WORMSS commented Jul 14, 2020

This breaks code..
I can no longer run the outputted javascript file in the browser

@RoystonS
Copy link

This breaks code..
I can no longer run the outputted javascript file in the browser

@WORMSS Is your problem the one I've just raised with webpack? (webpack/webpack#11536)

@WORMSS
Copy link

WORMSS commented Sep 26, 2020

@RoystonS no. I was not trying to export anything. I wasn't even trying to do a 'code' import.. just a 'type' import. So it seems

import type { SomeType } from 'SomePackage';
const a: SomeType = {};

is enough to class the whole file as a file that requires a module..
so the before output js would have been

const a = {};

now the output is

const a = {};
export {};

This means you cannot link directly to the compiled JS file to run synchronously in a web browser, or run the file in nodejs in classic mode
It has no idea what export {} means.. and just keels over.

My work around has been to just manually go into the compiled files and delete export {}.. but.. this is just not desirable when it used to work.

@rosdi
Copy link

rosdi commented Nov 2, 2020

May I know what the workaround to the problem raised by @WORMSS above? All our code breaks and we can no longer use the transpiled js file directly in the browser. We are not using webpack whatsoever.

Is there any setting in tsconfig that can prevent this new behaviour?

@WORMSS
Copy link

WORMSS commented Nov 2, 2020

Apparently if you use import type, it considers the file as a module, regardless of the fact you are not importing any code.
So, throw away types, and it works..
As far as I remember.

@jon49
Copy link

jon49 commented Nov 12, 2020

Yeah, this is unexpected behavior. If a person wants export {} they should explicitly type it into their code!

@jacoroux123
Copy link

jacoroux123 commented Nov 13, 2020

Still not sure why this is occurring I am simply importing an interface from a file and it is now emitting export{} because the file contains both the interface and its implementation.
This fully removes my ability to directly link to compiled JS files, which is my preferred method of doing this.
surely this could/should have been handled by a config setting.

@dcheung2
Copy link

dcheung2 commented Feb 9, 2021

I tried to it search it from release note and I found this thread.

I have an issue with older version of typescript that, when --isolatedModules is enabled, I cannot import a 'type only' ts file that export nothing else. and I found a workaround by export {} help in my case.

e.g.


// foo.ts
export interface foo{
  bar:string
}
// export {};

// bar.ts
import type { foo } from './foo'
const bar: foo = { bar:'foobar'};

👆 It failed on typescript 3.8

And then I spotted the behavior change since TS 4.0
https://www.typescriptlang.org/play?ts=3.9.7#code/KYDwDg9gTgLgBASwHY2FAZgQwMbDuiCAbwFgAoOOAI0ygC4BnGKZAc3IF8g

https://www.typescriptlang.org/play?ts=4.0.5#code/KYDwDg9gTgLgBASwHY2FAZgQwMbDuiCAbwFgAoOOAI0ygC4BnGKZAc3IF8g


Question.
Can I consider it is FIXED problem and it won't revert ?
Or should I consider the behavior may change anytime that I better hold myself not to upgrade ?

@rosdi
Copy link

rosdi commented Feb 9, 2021

I am not the project maintainer. But as I understand it, this is the new desired behavior. So feel free to upgrade.

It is possible a configuration flag will be added in tsconfig.json for those who want the old behavior though.

@WORMSS
Copy link

WORMSS commented Feb 9, 2021

Do we know if its possible to do a post typescript process hook or something? so we can do

fileContent.replace('export {}', '');

or something similar to get rid of this stupid behavior?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Committed The team has roadmapped this issue Suggestion An idea for TypeScript
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants