Repository: TypeScript
Issue: Allow importing both type entity and value entity for "const enum" through "import type"
Labels: Suggestion,Awaiting More Feedback

## 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 many `interface`, `type`, and `const 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 about `const enum`.

```ts
Original fileA.ts

import {
    TypeA, TypeB, TypeC,
    ConstEnumD, ConstEnumE,
} from './types.ts';

------------------

Current fileA.ts

import type {
    TypeA, TypeB, TypeC,
} from './types.ts';
import {
    ConstEnumD, ConstEnumE,
} from './types.ts';
```

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 write `require('./types.js')` into `fileA.js` though the generated code did not access any property of `require('./types.js')`. In other words, it makes `types.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 of `const 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` through `import type`

```ts
import type { ConstEnumA } from './types';
//       Type↓        Value↓
const a: ConstEnumA = ConstEnumA.propA;

// ts not emit "  require('./types')  "
```
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-import

```ts
// importsNotUsedAsValues is still on

import { ConstEnumA } from './types';
//       Type↓        Value↓
const a: ConstEnumA = ConstEnumA.propA;

// ts find just "ConstEnumA" is used and it is `const enum`
// not emit "  require('./types')  "
```

#### Breaking change?

Yes. Now TS do not emit `require('...')` though value-import exists in the code.


## Examples

A complete example of Solution 1:

```ts
import type {
    TypeA,
    TypeB,
    TypeC,
    ConstEnumA,
    TypeD,
    TypeE,
    TypeF,
    ConstEnumB,
    TypeG,
    TypeH,
    TypeI,
    ConstEnumC,
    ConstEnumD,
    TypeJ,
    TypeK,
} from './types';

const b: ConstEnumB = ConstEnumB.propA;
// compiler: OK, 0 error(s). not emit "  require('./types.js')  "
```

## Checklist

My suggestion meets these guidelines:

* [x] This wouldn't be a breaking change in existing TypeScript/JavaScript code
* [x] This wouldn't change the runtime behavior of existing JavaScript code
* [x] This could be implemented without emitting different JS based on the types of the expressions
* [x] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
* [x] This feature would agree with the rest of [TypeScript's Design Goals](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals).