TypeScript Version: 3.9.0-dev.20200222
Search Terms: export type * as namespace
Code
// provider.ts
export const foo = 42;
export interface Bar {};
// index1.ts
import type * as A from './provider';
export { A };
// index2.ts
export type * as A from './provider';
// 1.ts
import { A } from './index1';
const foo = A.foo;
type Bar = A.Bar;
// 2.ts
import { A } from './index2';
const foo = A.foo; // **expected an error, but succeed.**
type Bar = A.Bar;
Expected behavior:
index1.ts and index2.ts should have the same behaviour: export a namespace which only contain types, and the assignment to foo should result in an error.
Actual behavior:
In index2.ts the assignment succeed.
Playground Link:
Related Issues:
As per thi-ng/umbrella#209 it seems export type * from ... forms are also forbidden now, even though they worked fine in TS 3.8.2. Can we please have some more official clarity about which variations are allowed now?
they worked fine in TS 3.8.2
They were (accidentally) allowed, but they didn’t work fine—they were interpreted as a normal, non-type-only export, as the OP’s example shows.
The 3.8 blog post covers the feature in detail, but the TL;DR is:
Allowed imports:
import type Foo from 'mod'import type { Foo } from 'mod'import type * as foo from 'mod'Not allowed imports:
import type Foo, { Bar } from 'mod'import type Foo = require('mod')Allowed exports:
export type { Foo }export type { Foo } from 'mod'Not allowed exports:
export type * from 'mod'export type * as foo from 'mod'export type = Foo? Not even sure how you’d write the equivalent of an export = FooI will further say that I don’t personally have strong feelings against enabling export type *; rather, it simply didn’t contribute anything valuable to the problem set we were considering with this feature. I think it’s something we would consider adding if there’s high demand with compelling real-world use cases. I’m interested in hearing why a normal export * is problematic for you.
Thanks for the update @andrewbranch - I actually did read that blog post a while ago and could have sworn there was a mention of export type * from too, which I now can't find anymore... oh well... FWIW normal exports are going to be fine for my purposes, just wanted to better understand the valid options/reasoning before undoing the changes in my repo back to their pre-3.8.2 versions...
@andrewbranch I'm sorry, but I will have to ask for some more guidance from you about this export type behavior, especially with isolatedModules enabled:
As an example, let's take this project in question: @thi.ng/api. This linked index.ts file re-exports dozens of type definitions located in subfolders/files.
export * from ...) this will emit the full list of exports in JS, including (incorrectly, IMHO) dozens of empty JS files, which will be referenced from the transpiled index.js and are then breaking downstream builds (see note further below)export type { Fn, Fn2 ... } from "./api/fn") those export statements are correctly elided from the JS version, but that solution is infeasible & brittle since it requires maintaining a separate list of re-exports for each of the ~50 submodulesexport * from ...), the generated JS correctly only contains exports of the submodules which also contain actual JS valuesThe build issue mentioned above has to with that current form (1), my published project actually contains invalid (empty) ES modules (i.e. those which only contained type decls and IMHO should have been elided as with options 2 & 3) and building a userland project with rollup now fails because of those empty modules...
From that all, it seems my only viable option is to disable isolatedModules for building this upstream library project, but then again I'd first like to learn more about the interaction of export type and isolatedModules... (it's pretty confusing) thanks!
Addendum to the above comment: After some deeper enquiry, that build issue I've been encountering might actually have to do with snowpack (which uses rollup internally) and I've also submitted an issue with more details there: https://www.pika.dev/npm/snowpack/discuss/131
Still, I'd highly appreciate to get some more clarity on the correctness/expected behavior of using the different export types WRT isolatedModules in the earlier comment.
When you use --isolatedModules, you’re saying “ensure that my program can be compiled file-by-file, without any file knowing about another.” So under normal circumstances, when the compiler is running down your index.ts file with all the export *, it resolves information about the target module, and elides the export if there are no values exported from it. But in --isolatedModules, it doesn’t do that, because it’s simulating the circumstances under which a bundler might transpile your app on a file-by-file basis. So, it takes your export * at face value and emits it. This is long-standing behavior and not related to export type at all. And as you determined after tracing the build issue down to snowpack/rollup, re-exporting an empty module shouldn’t actually cause problems (tested with node 14 just now).
Admittedly, I didn’t know setting isolatedModules would actually _affect_ the emit rather than just _tell_ you about problems you’re going to hit when transpiling until looking into the behavior you’re describing. It does seem reasonable to want to check your program against --isolatedModules but also get the smartest emit we can offer. For now, I’d probably suggest that as part of your build, you run a transpiler-safety check via tsc --isolatedModules --noEmit, then actually emit the js with a second run where --isolatedModules is off. (I’m using command line flags here for brevity; of course you can make separate tsconfig.check.json and tsconfig.build.json files if you like.)
Thank you for the detailed explanation @andrewbranch and am sorry for the slight confusion. I was aware of most of this, but this weird build issue somewhat threw me / made me doubt... meanwhile we fixed the snowpack issue already and I will employ the --isolatedModules --noEmit combo for my project in the future. That's a great tip!
Most helpful comment
Thank you for the detailed explanation @andrewbranch and am sorry for the slight confusion. I was aware of most of this, but this weird build issue somewhat threw me / made me doubt... meanwhile we fixed the snowpack issue already and I will employ the
--isolatedModules --noEmitcombo for my project in the future. That's a great tip!