-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
Add --importsNotUsedAsValues=preserve-exact
#44137
Conversation
"category": "Error", | ||
"code": 1437 | ||
}, | ||
"'{0}' resolves to a type-only declaration and must be re-exported with a type-only re-export when 'isolatedModules' is enabled.": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The addition of this diagnostic for isolatedModules
is actually a bug fix which should have been included in type-only import work from the beginning.
@@ -1364,6 +1364,26 @@ | |||
"category": "Error", | |||
"code": 1433 | |||
}, | |||
"'{0}' is a type and must be imported with a type-only import when 'importsNotUsedAsValues' is set to 'preserve-exact'.": { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The predecessors to these messages try to say 'import type'
and 'export type'
instead of type-only import
and type-only export
, but these were worded with an eye toward allowing type-only import and export specifiers, as mentioned in the PR description. If we decide not to do that, I would reword these to say 'import type'
and 'export type'
since there would only be one syntax that can fix the error.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, the imported thing with this error could technically be an uninstantiated namespace, so “is a type” might be technically incorrect, but I think I prefer calling an uninstantiated namespaces a “type” to calling it an “uninstantiated namespace” in user-facing messages.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- is a type
- is a type-only entity
- is only used for type constructs
- is only used for type-checking
- is a typey boi
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"has no runtime representation"
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or, more specifically:
"'{0}' is a type or namespace that does not have an associated runtime value. It must be declared as a type-only import when 'importsNotUsedAsValues' is set to 'preserve-exact'."
For TL;DR, skip to 🦓 The design meeting raised questions about how the behaviors added here are organized into compiler options, specifically:
I’ll first discuss each of these questions, and then try to draw some conclusions from their answers.
🦓I think this leads to a conclusion: instead of introducing an option that says “leave my imports exactly as I’ve written them,” this PR should be reimagined as the compiler option described above in (4), one which, on its own, only prevents the elision of values that we believe can be elided due to lack of references in emitting positions. With all other settings at their defaults, types would still be elided so that emitted imports would function without runtime errors, and we wouldn’t have to add any additional checking errors in this scenario. But with the addition of This flag could still be a value of |
Thanks for providing all details here @andrewbranch I agree with the conclusion of introducing a new compiler option (e.g. The semantics of the full domain space discussed above are very subtle. Only TypeScriptistas will care or understand them. Most users just want a good safe default. So introducing a new option that has an easy adoption-path and sufficient consensus to become the ecosystem default is awesome. That's the winning argument for me. The opportunity to make the new option human-readable is a welcome bonus. If I understand correctly, you are saying that
Bike-shedding the name is the least important thing to me. I will be happy with anything you select. Here's some thoughts anyway 😉 I like to first think of how we might describe the option in the docs in a way that leads non-experts to helpful conclusions. The docs might say:
This option is turning off existing behavior. We are doing less. So I suggest something like |
Fixes #43393
This adds a new value for
--importsNotUsedAsValues
calledpreserve-exact
(name is bikesheddable), which is an emit mode where (non-type-only) imports and exports are never elided or transformed in any way. Every check and diagnostic added simply ensures that your imports and exports are plausibly valid as written at runtime:module
must be set toes2015
or higher, because asking for them to be converted to another module format and asking for them not to be messed with are obviously incompatible requestsexport
modifier on a type alias or interface declaration is still allowed)If this PR is merged, a follow-up PR will propose:
import { A, type B } from "./mod"
to make it possible to combine types and values in single import statements underpreserve-exact
preserve-exact
The work is not really complete without these tasks, but I thought splitting it at this point would make it would make it easier to review, as the code changes at this point are pretty small, but there are some points worth discussing:
isolatedModules
ensures that only one file at a time needs to be analyzed, but it still requires some semantic analysis within that file to determine if something is used in an emitting position. With this mode, transpilers could skip that work, for imports, and it seems like it would be convenient to extend that same benefit to exports. That said, it’s not clear how much transpilers really benefit from this convenience anyway, since they’re already set up to elide imports and exports that are not used in emitting positions today, which is presumably not a ton of overhead. (It would be interesting to hear if any Babel, esbuild, or swc folks have opinions on this.)