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

Generate dummy exports for exported types #1398

Closed
fwouts opened this issue Jun 27, 2021 · 4 comments
Closed

Generate dummy exports for exported types #1398

fwouts opened this issue Jun 27, 2021 · 4 comments

Comments

@fwouts
Copy link

fwouts commented Jun 27, 2021

Hi,

First off, thank you for creating esbuild. I'm using it through Vite and I'm astonished with the performance!

I'm aware that esbuild isn't designed to support type import statements such as import { SomeType } (instead one should use import type { SomeType }), especially when the type comes from a wildcard export. It's an understandable limitation to allow for files to be compiled independently.

In my current project, which needs to run on projects that don't have isolatedModules: true in their tsconfig, I managed to work around this limitation by running the TypeScript parser on each file before esbuild runs, generating fake exports for each type with the following hack:

const content = `${originalContent}
const __fakeValueExport__ = null;
export { ${findExportsWithNoValue(sourceFile)
        .map((e) => `__fakeValueExport__ as ${e}`)
        .join(", ")} };`;

findExportsWithNoValue finds each type export where there isn't also a value export of the same name (see gist).

For example, the following TypeScript file:

export type A = string;
export const A = "foo";
export type B = number;
export type C = number;
export const D = "bar";

would become:

export type A = string;
export const A = "foo";
export type B = number;
export type C = number;
export const D = "bar";
const __fakeValueExport__ = null;
export { __fakeValueExport__ as B, __fakeValueExport__ as C };

This means that type imports become effectively valid imports (even though they're unused).

Obviously, the hack I've implemented is far from efficient since it relies on running the full TypeScript parser on top of esbuild, for each file (although not within node_modules).

I was wondering whether this approach had been considered in esbuild? I understand it's a bit of a hack, but I thought it might help some tools that rely on ESBuild (e.g. Vite and Snowpack), since we could probably remove the limitation of isolatedModules: true.

I'd be happy to start a PR if there is interest in investigating this further.

@evanw
Copy link
Owner

evanw commented Jun 27, 2021

Sorry, I'm confused. It's deliberate that esbuild does not generate exports for types, since esbuild is a tool to convert TypeScript to JavaScript and types do not exist in JavaScript. Transforming TypeScript into TypeScript is not something that esbuild does. If you need to do that, you'll likely need to do something like what you're currently doing (using the TypeScript compiler API).

@fwouts
Copy link
Author

fwouts commented Jun 27, 2021

Sorry, I should have provided a more extensive example! I've created a sample repository that shows the issue and how I'm currently fixing it: https://github.com/fwouts/vite-type-import-example

To reproduce: run yarn dev and notice the following error in the browser logs:

Uncaught SyntaxError: The requested module '/src/button/Button.tsx' does not provide an export named 'ButtonProps'

This is caused by the following in src/button/index.ts where ButtonProps is a type, and therefore not a valid value to be exported, crashing the browser:

export { Button, ButtonProps } from "./Button";

To see the hack in action: uncomment the call to fakeExportedTypes() in vite.config.js.

The problem I'm trying to solve is that an export statement that re-exports a type becomes a runtime error in the browser, because that export doesn't exist once converted to JS. This workaround makes the export valid, without impacting program semantics (since the export isn't actually used in the generated JS code).

I believe this could be implemented in esbuild without impacting performance because we only need to parse top-level type export statements, there is no need to do any type resolution or any other complex computation.

Does this help clarify what I'm aiming to do?

@evanw
Copy link
Owner

evanw commented Jun 27, 2021

You are trying to go against the way TypeScript works. TypeScript is designed to remove types when converting TypeScript to JavaScript. If you want to do something different, you will have to write your own custom code. This isn't something that should be a part of esbuild because it's not part of TypeScript either.

which needs to run on projects that don't have isolatedModules: true in their tsconfig

Both esbuild and Vite require isolatedModules: true. See https://esbuild.github.io/content-types/#isolated-modules and https://vitejs.dev/guide/features#typescript. So if you really don't want to enable that, then you might not want to use esbuild or Vite. Either that or you will have to hack around it like you're doing. But doing that goes outside of the scenario for which these were designed, so you may experience difficulties.

@fwouts
Copy link
Author

fwouts commented Jun 28, 2021

Yup, I was expecting that answer. I'll keep using this hack instead, thanks for the response! 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants