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

isolatedModules refusing to export a type created in the same file #28481

Closed
voliva opened this issue Nov 12, 2018 · 20 comments
Closed

isolatedModules refusing to export a type created in the same file #28481

voliva opened this issue Nov 12, 2018 · 20 comments
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@voliva
Copy link

voliva commented Nov 12, 2018

TypeScript Version: 3.2.0-dev.20181110

Code

File 1:

export const myVar = {
   foo: 'foo',
   bar: 5
};

File 2:

import { myVar } from './file1.ts';

type MyType = typeof myVar;

export { MyType }

Expected behavior:
Code can transpile with --isolatedModules flag

Actual behavior:
Transpile is rejected with error:

Cannot re-export a type when the '--isolatedModules' flag is provided.

Related Issues:
#21194

In that issue, in a comment it's explained that

We can't compile [...] without knowing whether MyType is a type. If it is, we compile to nothing. If it isn't, we compile to exports.MyType = module1_1.MyType;
The --isolatedModules flag is intended to prevent writing code that can't be transpiled without information about other modules, so it's working as intended in this case.

However, in this other case, MyType can only be a type. This also happens when using things like ReturnType, Pick, etc.

This raises up another question: What happens if in a file we have something like:

import { MyType } from './file2.ts';

type MyTypeAlias = MyType;

export { MyTypeAlias }

In this case, MyTypeAlias can also only be a Type - So by that logic we should be able to transpile this file in isolation (by just not emiting any export).

@weswigham weswigham added Suggestion An idea for TypeScript In Discussion Not yet reached consensus labels Nov 12, 2018
@brandontle
Copy link

brandontle commented Nov 23, 2018

I'm having a similar issue which is preventing the "re-export" of an interface.

Case 1 — Doesn't work:

import IUser from './user.ts';

interface IStateProps {
    user: IUser;
}
interface IDashboardProps extends IStateProps{};

export { IDashboardProps };
//Error: Cannot re-export a type when the '--isolatedModules' flag is provided.

Case 2 — Works:

import IUser from './user.ts';

interface IStateProps {
    user: IUser;
}
export interface IDashboardProps extends IStateProps{};
//Transpiles successfully 

Is this an intentional constraint on how interfaces should be exported? Why would exporting as in Case 1 not be allowed?

@oriSomething
Copy link

Partly related, but maybe it would be useful if TypeScript will support import type as in Flow.
As much as I hated it when I've used Flow, it seems very useful for isolatedModules. Because, sometimes you have no choice to use isolatedModules, And not be able to re-export type is quite limited for me personally

oriSomething added a commit to oriSomething/no-export-duplications that referenced this issue Nov 28, 2018
@voliva
Copy link
Author

voliva commented Jan 11, 2019

OK, I found a really weird workaround, which is basically an extension of @brandontle's - Needs some clarification....

This isolated-modules warning/error will only appear when exporting through export { ... }. So, workaround, change the examples from the original issues for:

import { myVar } from './file1.ts';

export type MyType = typeof myVar;

And

import { MyType } from './file2.ts';

export type MyTypeAlias = MyType;

What I think is that on a export { .... } node, all the variables inside are being treated as possible values, which is what is being mentioned as #21194 is not possible to do for types (as types doesn't get exported when transpiling) and so the warning is shown.
But at the same time it's able to detect that it's indeed a type when exporting inline with export type or export interface, and so it just gets ignored when transpiling.

What's really weird though, is that you don't need to rename it or give it an alias:

import { MyType } from './file2.ts';

export type MyType = MyType;

This will work, although it reads as recursive type redefinition, but somehow tsc understands this as just reexporting that type.

@Kizmar
Copy link

Kizmar commented Jan 14, 2019

Seeing something similar. In my case I have a couple things being exported from a file, including an interface. When I try to export select parts of the component file in the index.ts barrel, I run into this issue.

The component file looks something like this:

export interface ISubNavigationItem {
	...
}

export class SubNavigationComponent extends React.Component<ISubNavigationComponentProps, ISubNavigationComponentState> {
        ...
}

export const SubNavigation = SubNavigationComponent;

In the index.ts file, because I don't want to export SubNavigationComponent I tried...

export { SubNavigation, ISubNavigationItem } from './sub-navigation.component';

...but get...

[ts] Cannot re-export a type when the '--isolatedModules' flag is provided. [1205]

My workaround was to just use:

export * from './sub-navigation.component';

@bzums
Copy link

bzums commented Jan 29, 2019

Any progress here, this behaviour is not expected, is it?

@bbbryan14
Copy link

bbbryan14 commented Apr 4, 2019

I'm running into this exact same issue...


interface path {
  path: string;
}

interface container {
  route: path;
}

export {
  container,
  path
}

@RyanCavanaugh RyanCavanaugh added Working as Intended The behavior described is the intended behavior; this is not a bug and removed In Discussion Not yet reached consensus Suggestion An idea for TypeScript labels Apr 4, 2019
@RyanCavanaugh
Copy link
Member

This is the intended behavior.

The intent of the --isolatedModules flag is to say "Make sure my program can be correctly transpiled by a transpiler that doesn't do any kind of typechecking". In practice this means Babel, for example.

If you compile the OP example with TS, you'll see that MyType is elided from the output.

If you run the sample example in the Babel repl, you'll see this error because Babel doesn't see any value named MyType.

/repl: Export 'MyType' is not defined (5:9)

  3 | type MyType = typeof myVar;
  4 | 
> 5 | export { MyType }
    |          ^

So TypeScript is correctly identifying that this program can't be transpiled by e.g. Babel, which is the point of the flag.

@typescript-bot
Copy link
Collaborator

This issue has been marked 'Working as Intended' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

@exapsy
Copy link

exapsy commented Jun 20, 2019

@RyanCavanaugh Thanks for the explanation, that makes sense.
Although, couldn't the Typescript compiler, just compile it the right way so Babel won't complain?

So if it detects any kind of interface in an export { }, it will just transpile it into an empty export. Like it would do with the import.

It's really weird and a little bit unorthodox to not to be able to export it the usual way on the bottom of the page along with the other exports, and as you probably can see this issue has gained plenty of attention in many other repositories.
But if it's hard enough to do such thing, it's alright I guess. But still weird and this issue will probably continue to gain traction in the future, because it will still be a small issue.

@9SMTM6
Copy link

9SMTM6 commented Jun 20, 2019

@Eksapsy The point of this flag is to be used on code that doesnt get transpiled by TypeScript.

TypeScript was first of all invented to add types to JavaScript, Polyfills and that stuff are secondary to that. The TypeScript transpiler was never meant to be doing much more than stripping TypeScript code of the types and adding a few select Polyfills.

If you want more freedom you want to use other transpilers, with Babel being the most popular one. Now, stripping the types from TypeScript code turns out to be easier and giving more freedom than queing both Babel and TypeScript transpilers together, thus in a lot of situations (to give one example, create-react-app with TypeScript code) what ends up happening is that your TypeScript code doesn't get transpiled by TypeScript at all, only type-tested, and the stripping of the types and Polyfills (often orienting by the Polyfills the TypeScript compiler adds) is done by Babel.

I guess Babel is not willing or at all able to figure out when you export a type and when a value, and thus wouldn't be able to handle that code without freaking out.

Anyways, why that doesn't work with Babel isn't really all that important, what's important is that if it can handle it or not is out of the hands of the guys at TypeScript, and thus providing this flag to guard against errors is the best thing they can do.

@exapsy
Copy link

exapsy commented Jun 20, 2019

@9SMTM6 Yeah that makes sense. I didnt really know that in some situations the typescript code wasn't getting transpiled to regular javascript. Good to know.

I guess the flag is pretty important at that situation then and the whole issue is for Javascript to blame as an entity. 😄 We really need typescript to make its own web assembly based language so we can rid off JS 😅 Just kidding, or am I.

@nickform-ost
Copy link

For anyone else who gets here, it looks like this part of @voliva's comment from 11 Jan 2019 is no longer true as of Typescript 3.7:

What's really weird though, is that you don't need to rename it or give it an alias:

import { MyType } from './file2.ts';

export type MyType = MyType;

This will work, although it reads as recursive type redefinition, but somehow tsc understands this as just reexporting that type.

This is explicitly addressed in the blog post announcing version 3.7 (here).

@oriSomething
Copy link

It's possible to do this now:

export type MyType = import("./file2.ts").MyType;

But generics should be duplicated

@nickform-ost
Copy link

Thanks @oriSomething - just tested your suggestion and it works.

@delanni
Copy link

delanni commented Apr 16, 2020

I can see the reasoning behind the flag, I just don't completely agree with it. --isolatedModules is useful when we use Babel to transpile our code, but still, for my code to check out from the types' perspective (which is why we use typescript), I still need to propagate type definitions through my codebase, so I need to import and export types.
Now because of this error, I cannot use this flag, which just means, I'd lose all the other aspects of safety this would guarantee when transpiling with babel.

@mikeaustin
Copy link

I just ran into this and am still not clear about what happens. If I do

type _State = { ... };
...
export type State = _State"

then it works as expected. Why does that work but not exporting State directly?
People looking at this file will wonder what is going on. Or, I could just disable isolatedModules.

@kevinclarkadstech
Copy link

kevinclarkadstech commented Jul 12, 2020

I am having an issue in version 3.9.3 with this error, I wanted to have a type declared but only export outside of the file the properties I want provided:

type Mutation<T extends { id: string }> = { docId: string; docPath: string; operation: 'add' | 'update' | 'remove'; data?: T | Partial<T>; merge?: boolean; created: Date; }

type MutationInterface<T extends { id: string }> = Pick<Mutation<T>, 'docId' | 'docPath' | 'data' | 'merge' | 'operation'>;

export {MutationInterface as Mutation};

It was working in my Stencil JS project but when I copied it over to a React version it suddenly was giving me this error.

@bozdoganCihangir
Copy link

Well i had the same issue but after i have noticed i forgot to put .d.ts extension.

@softmarshmallow
Copy link

Having this on NextJs and re-exporting interfaces from index.ts

@LeandrodeLimaC
Copy link

If you came to this issue with the following error

Re-exporting a type when the '--isolatedModules' flag is provided requires using 'export type'.

Theres a chance that you didn't notice the last part of the phrase
You must explicit export your types in the following manner

export type { MyType }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests