-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Allow importing both type entity and value entity for "const enum" through "import type" #40344
Comments
import type { ConstEnumType } from ..
I'm a English learner. I would be happy to correct my expression if someone willing to remind me. |
I don't see any tractable way to square this circle. |
Talking offline with @andrewbranch, this does seem like something where we could just ignore the warning we "should" issue when a value reference occurs. This would still break the transpile scenarios that |
I think we can look at this problem in a different way. Let's review the scenario in which Then the So, I think we only need to change the definition of |
That is a pretty good description of how |
Since you are gauging demand for it, we are also running into this in the Azure Portal. We have a ton of dynamically loaded modules (where |
As an additional data point, I'm investigating adopting Vite as a dev build tool for Outlook. However, it builds each module individually, so I need to enable |
…sManager This change extracts the IssuesManager Events enum into a separate file to break the circular dependency of values between IssuesManager.ts and SourceFrameIssuesManager.ts. This helps reduce existing circular dependencies, in theory moving us slightly closer to being able to warn about such cycles at build-time to avoid unexpected errors. This particular cycle recently led to a runtime error when attempting to subclass IssuesManager to introduce additional functionality. This subclass was included in the cycle as SourceFrameIssuesManager was updated to import the subclass instead of IssuesManager. At the time the Events enum for IssuesManager was non-const, causing the circular dependency to exist post-build. The error occurred because the extends relationship of the subclass was evaluated during module initialization. Since then the Events enum has been converted to const which eliminates the runtime error. However, eliminating even this cycle is still beneficial. The compile-time only relationship is not obvious and cannot otherwise be communicated explicitly until values from const enums can be used with `import type` per microsoft/TypeScript#40344. Bug: 1229328 Change-Id: I033080c77f22584aa0527bcd0559698341d200f5 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/3040178 Commit-Queue: Tony Ross <[email protected]> Reviewed-by: Sigurd Schneider <[email protected]> Reviewed-by: Tim van der Lippe <[email protected]>
Adding this ability would really benefit us where I work at as well. |
Just adding a datapoint here as well. Our project makes extensive use of a large number of enums in a dependency, and this is directly impeding our efforts to bundle-split that dependency until it's actually needed, instead of just being needed to grab a constant string. |
I am in a similar boat where our types come from an extremely large auto-generated package (60mb+ of JS, 8mb of types). I can use
|
Here is a novel workaround I came up with. Be aware that this is hacky and may not be to your personal tastes, but just posting in order to share. // Suppose this enum was imported using `import type` and thus we cannot directly use it as a value
enum TestEnum {
FOO = "FOO",
BAR = "BAR",
BAZ = "BAZ",
}
/**
* Due to https://github.com/microsoft/TypeScript/issues/40344, we cannot declare an object that uses
* TestEnum as a value, like so
* {
* a: TestEnum.FOO,
* b: TestEnum.BAR
* ...
* }
*
* So instead, lets make an exhaustive map of the enum type, which is the opposite of the desired mapping
* above. A compiler error will be thrown if any values are added/removed from TestEnum, so we can ensure
* we are somewhat type safe here.
*/
const initialMap: Record<TestEnum, string> = {
FOO: "a",
BAR: "b",
BAZ: "c",
};
/**
* Now, invert that mapping so we now have a map of the your type to enum values, which emulates using
* the enum as a value.
*/
const invertedMap: Record<string, TestEnum> = Object.keys(initialMap).reduce((res, key) => {
// We have to ignore the typescript error here as we are definitely doing some janky stuff
// @ts-ignore
res[initialMap[key]] = key;
return res;
}, {} as Record<string, TestEnum>);
// Tada! We have a mapping of string to enum value while only using the enum as a type rather than a value
console.log(invertedMap["b"]); // Logs: 'BAR'
// NOTE: This works well for string enums, but number enums will have their values cast to strings by `Object.keys`
// You could `parseInt()` on these at your own risk... |
Do you need ambient const enums (e.g. const enums in
My take:
|
This was definitely a surprise to me as the const enum values are inlined everywhere at compile time, and so really their usage is by design decoupled from the origin module at runtime. Having to try to work out ways around this. |
We would love this change. Here is the scenario where it comes into play for us. We generate d.ts files from our C# classes. The generated typing files output const enums from our C# enums. We reference these enums all over our typescript code. With this approach we do not need to keep two versions of our enums in sync (one for C# and one for typescript). Since converting our typescript to modules, our options for compiling our project using these const enums is now very limited. We are currently using Webpack which is able to handle the compilation. However, small changes to files can take a while to rebuild when using webpack. The ts-loader transpileOnly option, though wildly faster, is not able to inline the values of these ambient const enums and obviously breaks things. The fork-ts-checker-webpack-plugin suffers from the same issue for presumably the same reason. A way forward with this would be greatly appreciated. |
Hello there, It would benefit us as well to allow Otherwise we would have to duplicate the values. Thank you, |
I'm coming rather late to the discussion, but let me add a suggestion. Since the argument for not implementing this feature is that it would change the semantics of either type-import or value-import, why not introduce a new syntax for importing and exporting compile-time constants? For example: import const { MyEnum1 } from 'x';
import { const MyEnum2 } from 'x'; and: export const { MyEnum1 };
export { const MyEnum2 }; This would make very clear the distinction between TS types, TS constants and JS bindings. |
Search Terms
const enum, import, importsNotUsedAsValues
Scenario
I am a library developer, and there is a really large
types.ts
file in my project, which contains manyinterface
,type
, andconst enum
, defining a large part of types of the project, just like you do (typescript/src/compiler/types.ts
).Recently I turned on the compiler option
importsNotUsedAsValues
, and found some problems aboutconst enum
.Because the entities of
enum
belong to both type scope and value scope, I have to import them separately if I want to use them as values.Now I have to separate the import of type
const enum
from a lot of names file by file, which is really a lot of work.What's worse, after turning on
importsNotUsedAsValues
, TypeSciprt unnecessarily writerequire('./types.js')
intofileA.js
though the generated code did not access any property ofrequire('./types.js')
. In other words, it makestypes.ts
seems to have side effects. And this feature may affect the file reference relation of the project and lead to incorrect Rollup order.In my mind, accessing the
const enum
entity in value scope is a kind of special behavior and is something that will be replaced in compile-time. There should be a way to import both type entity and value entity ofconst enum
from a file without really requiring the file, especially when the file really has side effects.Solution
There are 2 ways to solve this problem:
1. import both type and value for
const enum
throughimport type
I prefer this method because I will not need to greatly change my code.
Breaking change?
Name conflicts may occur if there was already a name in value scope which was same with 'ConstEnumA',
but I think this is very very rare and will be prevented by linter usually. An extra compiler option can be provided to solve this.
2. check
const enum
in value-importBreaking change?
Yes. Now TS do not emit
require('...')
though value-import exists in the code.Examples
A complete example of Solution 1:
Checklist
My suggestion meets these guidelines:
The text was updated successfully, but these errors were encountered: