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
[Babel 7] [Typescript] Exported Interfaces from an import are kept in the generated JS code #8361
Comments
Hey @manrueda! We really appreciate you taking the time to report an issue. The collaborators If you need any help, or just have general Babel or JavaScript questions, we have a vibrant Slack |
What if it isn't an interface but a class? Since Babel works per-file and not per-project, it can't know. |
Works with classes. But the example that i showed is a simplified version. In my real life problem that interface comes from an external package (npm) so i don't have access to change it to a class. |
How does typescript handle it with the |
Yeah that case is not supported by typescript, it gives this error: What makes sense if the modules are treated in an isolated way. I will have to find another way to structure and export my types. |
One option might be to do something like this so it's syntactically clear that import * as FileA from './file-a';
export type ExportedInterface = FileA.ExportedInterface;
export function test(): ExportedInterface {
return { value: '1' };
} |
I think I'm running into the same problem and @dfreeman's workaround works. // fileA.ts
export interface ExportedInterface { } // fileB.ts
import { ExportedInterface } from './fileA';
export { ExportedInterface }; Webpack complains that export 'ExportedInterface' was not found in This makes both happy: // fileB.ts
import * as FileA from './fileA';
export type ExportedInterface = FileA.ExportedInterface; I assume the problem is Babel not stripping off imports / exports of |
In order to use TypeScript with the new @babel/preset-typescript we need to remove the flow-preset and add / change the decorators and class-properties plugins. With this change stripping types from .tsx? files mostly works. The only issues that I have found are: - babel/babel#7749 - babel/babel#8361
The babel typescript preset does not work exactly as we want it to. from: #613 (comment) ``` After discussing with @robin-drexler we decided to switch back to using ts-loader and typescript as we did before in this preset. Because with the Babel variant there are two kind of important features missing (type imports and interface exports / re-exports) - at least that we know of now. Other reasons for reverting: - The babel variant will probably always be running a bit behind in terms of new features - chasing bugs between babel / typescript etc and not knowing exactly where they come from - using ts-loader and typescript does work but there is no real benefit for us to do the switch to babel (at least for now) ``` - babel/babel#7749 - babel/babel#8361
The babel typescript preset does not work exactly as we want it to. from: #613 (comment) ``` After discussing with @robin-drexler we decided to switch back to using ts-loader and typescript as we did before in this preset. Because with the Babel variant there are two kind of important features missing (type imports and interface exports / re-exports) - at least that we know of now. Other reasons for reverting: - The babel variant will probably always be running a bit behind in terms of new features - chasing bugs between babel / typescript etc and not knowing exactly where they come from - using ts-loader and typescript does work but there is no real benefit for us to do the switch to babel (at least for now) ``` - babel/babel#7749 - babel/babel#8361
The babel typescript preset does not work exactly as we want it to. from: #613 (comment) ``` After discussing with @robin-drexler we decided to switch back to using ts-loader and typescript as we did before in this preset. Because with the Babel variant there are two kind of important features missing (type imports and interface exports / re-exports) - at least that we know of now. Other reasons for reverting: - The babel variant will probably always be running a bit behind in terms of new features - chasing bugs between babel / typescript etc and not knowing exactly where they come from - using ts-loader and typescript does work but there is no real benefit for us to do the switch to babel (at least for now) ``` - babel/babel#7749 - babel/babel#8361
I can confirm that I have this issue using TypeScript and re-exporting interfaces as well. |
Importing types, and then re-exporting them from another file is not allowed in Babel, and will never work. Babel has no context in whether an import is a type or a value, and as such, treats them all as values. You'll need to restructure your application to work around this. |
That is very unfortunate. The use case is that I create a folder to encapsulate the API calls from the React web app into a single folder. All the interfaces for interacting with the API are in that folder organized into various files. To make using this API in the React app easier, I think import and export those from an |
It is possible to re-export values though and in fact Material UI uses Babel to do so. However, it's impossible to re-export types in Babel, since (as the GitHub issue shows) it will cause these types to end up within the JavaScript output and this shall cause runtime errors. Babel transpiling TypeScript should be treated as if you are in I don't think it would be possible for Babel to determine whether an import is a type or a value (or both) without it having read the underlying types files, and presumably this would require something more complex than a syntax and transform plugin. Of course, technically if there were a way to temporarily strip lines with pure TypeScript exports before running them through Babel that would work. I've done this on one of my own projects using a rollup plugin to automatically remove lines which I prefixed with a particular comment. That worked but it is a hack. |
@TroySchmidt What about this? |
@milesj That does seem to work. So I will continue down that path. TypeScript classes could include additional functionality with methods on them. But these are used to define the interfaces of the JSON data that is loaded from the api. Using interface made sure that someone doesn't accidentally try to add class style functions and expect those to work. |
@tajo , like @milesj mentioned, instead of importing types and then re-exporting, simply do @TroySchmidt importing types and then re-exporting shouldn't be an issue for the use case that you're describing. Back to the use case in the topic. In general I don't consider it a good practice to import a type/interface/something, use it and then (re-)export it from that same file. That sounds like some real spaghetti code. Here's what I consider a better way of structuring your code, with emphasis on types. If you have a folder per React component, you could have one file This way of organizing your types is of course not tied to React, but can be used for all purposes: root/src/utils/types.ts:
root/src/utils/index.ts:
root/index.ts:
|
@sorenhoyer but |
@dfreeman Can I somehow reexport
|
having file like, works well: export interface Interface {}
export {}
|
@gavar Oh, that's a neat workaround. Should be pretty straightforward to add this into the ts plugin. |
Yeah, that is pretty neat @gavar. Silly me wrote a codemod to transform everything to |
I've made a very simple plugin for that, it doesn't have it's own module, but feel free to copy. import { NodePath, PluginObj } from "@babel/core";
import { TemplateBuilder } from "@babel/template";
import { ExportDeclaration, ExportNamedDeclaration, Program, Statement } from "@babel/types";
interface Babel {
template: TemplateBuilder<Statement>;
}
interface State {
filename: string;
}
/**
* Adds stub `export { }` when module does not have any actual exports.
*/
export function InjectEmptyExports(babel: Babel): PluginObj<State> {
const stub = babel.template("export { }")();
return {
visitor: {
Program(root: NodePath<Program>, state: State) {
const filename = state.filename.split("\\").join("/");
if (filename.endsWith(".d.ts")) return;
if (filename.includes("/node_modules/")) return;
// check if any node is actual export declaration
let empty = true;
root.traverse({
ExportDeclaration(path: NodePath<ExportDeclaration>) {
if (empty)
empty = isTypeDeclaration(path.node);
},
});
// add empty exports definition
if (empty)
root.node.body.push(stub);
},
},
};
}
function isTypeDeclaration(node: ExportDeclaration) {
if (node.type === "ExportNamedDeclaration")
switch (node.declaration.type) {
case "TSTypeAliasDeclaration":
case "TSInterfaceDeclaration":
return true;
}
} |
I am facing a somewhat opposite issue trying to get React Styleguidist with So, with apologies for the tangent of a comment, I want to leave a breadcrumb here for anyone else who comes here while googling with this issue. The trick of using e.g. |
Here's another solution that neither needs a plugin nor export *, which for us wasn't feasible: before export { BuerliState } from './store/states/buerli' after import { BuerliState as BS } from './store/states/buerli'
export type BuerliState = BS |
It looks like the real fix will be using "type-only imports and exports" in the next version of TypeScript (3.8). However, that won't stop people from accidentally importing types using normal imports and exports and potentially breaking a build output transpiled with Babel. I wish there was a way of switching off the ability to import and export types using normal JavaScript syntax? /cc @DanielRosenwasser Or, if there won't be a |
See |
This will be fixed in the next minor version: you will be able to use |
@nicolo-ribaudo has that minor version been released? |
Not yet. You can watch #11171 to ge notified when it's released. |
That would explain things. Thanks so much! |
Bug Report
Current Behavior
If the Typescript code exports an interfaces that was imported from other module, that interface reference is kept in the Javascript code after transpilation.
Input Code
File A:
File B:
Expected behavior/code
The result code should not keep reference of that interface because is a type.
Babel Configuration (.babelrc, package.json, cli command)
Environment
Possible Solution
Looks like the here is a comment in the code that could be related to this issue: https://github.com/babel/babel/blob/master/packages/babel-plugin-transform-typescript/src/index.js#L52
Additional context/Screenshots
The generated code currently is the following one:
File A:
// No error, the file is generated empty
File B:
The text was updated successfully, but these errors were encountered: