| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
Sorry, something went wrong.
|
Andrew Branch (@andrewbranch) what about imported values that are only used for their types via typeof in the file? Are these imports still elided? |
Sorry, something went wrong.
|
Klaus Meinhardt (@ajafff) I think ideally the plan would be no, imports not marked with type are never elided. Flow has an import typeof form for this use case. I think that’s probably a reasonable follow-up feature. I had initially thought of import typeof as syntactic sugar for something already possible, but as you bring up, if you care about eliding imports that are unnecessary at runtime but you need the typeof a value, the original proposal doesn’t allow for that. /cc Daniel Rosenwasser (@DanielRosenwasser) thoughts? Of course, a workaround is to export a type alias from the file where the value was exported and import that instead, but you can’t do that if the value in question comes from a third party library. Edit: a surefire workaround is typeof import('./mod').SomeClass |
Sorry, something went wrong.
This syntax was added in TypeScript 3.8. The supported forms are from the feature pull request[1]. [1]: microsoft/TypeScript#35200
|
Maybe I'm missing something and I'm not sure where to post this remark, but should import type really make the importing ts file a module? |
Sorry, something went wrong.
|
I think that’s a reasonable question, and I did think about it while writing this feature. I’ll try to explain my thought process.
So first, just to clear up the background and definitions—it sounds like you probably know this, but just making sure the grounding for the rest of my argument is established. When we’re talking about JavaScript you’re correct: it is true that an import or export makes a file a module. However, the converse is not true. A file without an import or export is both a valid module and a valid script. But for the purposes of type checking, we have to make a decision about how to treat every file. So if we don’t see any imports or exports, we unequivocally treat the file as a script—it’s easy and harmless to add export {} if you don’t want the file to be a script. Now, consider that we’ve always elided unused imports from our JS emit. (As of this PR, that’s configurable, but the default is still to elide unused imports.) If you write a TypeScript file that looks obviously module-like because you imported something, but then never used it in a value position, the output JS will look like a valid script. Does that mean we should treat the TypeScript file as a script? I don’t think so. The source file must be parsed as a module, and the output can be parsed as a module, so treating the file as a module seems like the best choice. When you write an import type declaration, it’s essentially the same as writing an import declaration that you never use in an emitting position, except that it’s enforced that you never use it in an emitting position. Now, since import type is not standard ES grammar, I think you could reasonably argue that we could implement whatever rules we wanted; that it would not be inherently incorrect to say that import type does not constitute a module. But ultimately, given how similar it is to writing a regular import of a type, I think maintaining consistency that all import declarations are module markers was the correct choice. That said, I 100% agree with you that referencing modules in scripts is painful—I’ve hit that exact problem before. But I don’t think import type would have been the best solution, even if we hadn’t already settled on the current behavior. |
Sorry, something went wrong.
|
This wasn't my question, but I just wanted to give props to Andrew Branch (@andrewbranch) for such a thoughtful and clear answer. Well done sir! |
Sorry, something went wrong.
|
Well, it was my question, and although the outcome is maybe not what I wanted to hear, I must second that this is a very thoughtful and clear answer and I agree 100%. Thank you, much appreciated! |
Sorry, something went wrong.
- fixed react-i18n duplicated dependency - removed all unused dependencies - update webpack configs to use babel/preset-env and ensure the right plugins are loaded chore: fix issue with babel warnings when re-exporting types - refer to microsoft/TypeScript#35200 and babel/babel#10981
While it's a bit late, I still want to point out that the wording is a bit confusing.
I hope this could help someone like me who is interested in the history of deprecated options covered by the new verbatimModuleSyntax option in the future. All in all, I like TypeScript :) |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
TL;DR:
To do:
Background
TypeScript elides import declarations from emit where, in the source, an import clause exists but all imports are used only in a type position [playground]. This sometimes creates confusion and frustration for users who write side-effects into their modules, as the side effects won’t be run if other modules import only types from the side-effect-containing module (#9191).
At the same time, users who transpile their code file by file (as in Babel, ts-loader in transpileOnly mode) sometimes have the opposite problem, where a re-export of a type should be elided, but the compiler can’t tell that the re-export is only a type during single-file transpilation (#34750, TypeStrong/ts-loader#751) [playground].
Prior art
In early 2015, Flow introduced type-only imports which would not be emitted to JS. (Their default behavior, in contrast to TypeScript’s, was never to elide imports, so type-only imports for them were intended to help users cut down on bundle size by removing unused imports at runtime.)
Two months later, #2812 proposed a similar syntax and similar emit behavior for TypeScript: the compiler would stop eliding import declarations from emit unless those imports were explicitly marked as type-only. This would give users who needed their imports preserved for side effects exactly what they wanted, and also give single-file transpilation users a syntactic hint to indicate that a re-export was type-only and could be elided: export type { T } from './mod' would re-export the type T, but have no effect on the JavaScript emit.
#2812 was ultimately declined in favor of introducing the --isolatedModules flag, under which re-exporting a type is an error, allowing single-file transpilation users to catch ambiguities at compile time and write them a different way.
Since then
Over the last four years after #2812 was declined, TypeScript users wanting side effects have been consistently confused and/or frustrated. They have workarounds (read #9191 in full for tons of background and discussion), but they’re unappealing to most people.
For single-file transpilation users, though, two recent events have made their lives harder:
In TypeScript 3.7, we sort of took away --isolatedModules users’ best workaround for reexporting a type in Prevent collision of imported type with exported declarations in current module #31231. Previously, you could replace export { JustAType } from './a' with
But as of TypeScript 3.7, we disallow the name collision of the locally declared JustAType with the imported name JustAType.
If a Webpack user was left with an erroneous export { JustAType } from './a' in their output JavaScript, Webpack 4 would warn, but compilation would succeed. Many users simply ignored this warning (or even filtered it out of Webpack’s output). But in Webpack 5 beta, Tobias Koppers (@sokra) has expressed some desire to make these warnings errors.
Proposal
Syntax
Supported forms are:
Possible additions but I think not terribly important:
We notably do not plan to support at this time:
The forms in the former bullet will be syntax errors; the forms in the latter will be grammar errors. We want to start with productions that can be read unambiguously, and it’s not immediately clear (especially in the absence of Flow’s implementation), what the semantics of import type A, { B } from './mod' should be. Does type apply only to the default import A, or to the whole import clause? We prefer no one need wonder.
Type semantics
Any symbol with a type side may be imported or exported as type-only. If that symbol has no value side (i.e., is only a type), name resolution for that symbol is unaffected. If the symbol does have a value side, name resolution for that symbol will see only the type side. The typical example is a class:
If the symbol is a namespace, resolution will see a mirror of that namespace recursively filtered down to just its types and namespaces:
Emit
Updated: When the importsNotUsedAsValue flag is set to 'preserve', type-only import declarations will be elided. Regular imports where all imports are unused or used only for types will not be elided (only the import clause will be elided):
Back-compat flag
There’s a new flag removeUnusedImports. Its name is not perfect because it really means “remove imports that have imported names that never get used in a value position.” Open to suggestions.
Updated: this PR is backward-compatible by default.
Auto-imports behavior
I’m not yet confident what other changes, if any, will the right move, but the main scenarios to consider are:
Successor of #2812
Closes #9191
Closes #34750
Would close if they were still open: