Skip to content

Allow use of const enums in value space when preserveConstEnums=true #51530

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

Open
jakebailey opened this issue Nov 14, 2022 · 1 comment
Open
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript

Comments

@jakebailey
Copy link
Member

Background

The TypeScript compiler uses const enums for the performance benefit of inlining, but then enables preserveConstEnums and modifies the output d.ts files to "lie" to API consumers and say that the enum is not const. This, and "just don't export const enums" are the recommended methods for library authors; the emitted code for const vs non-const enums is identical with the flag enabled.

Internally, however, we use the enum values at runtime for Debug helpers. In order to make this work, we must write code like:

export function formatSyntaxKind(kind: SyntaxKind | undefined): string {
    return formatEnum(kind, (ts as any).SyntaxKind, /*isFlags*/ false);
}

But, we cannot write:

export function formatSyntaxKind(kind: SyntaxKind | undefined): string {
    return formatEnum(kind, SyntaxKind, /*isFlags*/ false);
}
// or
export function formatSyntaxKind(kind: SyntaxKind | undefined): string {
    return formatEnum(kind, (SyntaxKind as any), /*isFlags*/ false);
}

Without using // @ts-ignore-error to silence:

'const' enums can only be used in property or index access expressions or the right hand side of an import declaration or export assignment or type query

Proposal

Don't issue the above error when preserveConstEnums=true.

I believe that this change should be safe because this flag explicitly forces tsc to emit enums as though they had been written as non-const; not issuing the error lets you use it as a value.

Motivation

In the codebase pre-modules, ts was the namespace and was sometimes used intentionally, but now, this ts as any pattern is one of the only reasons why our codebase internally needs to refer to the namespace barrels and can't direct import everything everywhere.

If this error were disabled when preserveConstEnums=true, however, we could just use the enums directly and not have to jump through hoops (and potentially pull enum formatting out and drop the helpers, removing a cycle between debug and types).

Alternative considered

Another alternative to this (for the TS compiler itself) is to just not use const enums. This works because our current outputs are produced by esbuild, which doesn't care at all about whether or not enums are const; it will inline them if it can, and it will produce a non-const enum object if the enum itself is needed in value space. But, if we do any other kind of build that isn't "esbuild on src" (e.g. change bundlers, run the bundler on tsc outputs, etc), using non-const enums would cause a performance regression. I also have no idea if there are behaviors one can have in const enums and not in regular enums, though I suspect there aren't any.

Related

@jakebailey
Copy link
Member Author

Just to provide a data point for the future, we currently don't let you use a const enum in value space when imported via a import type, which is good. But this issue would require that we still allow const enums in non-type imports, otherwise you break things like esbuild.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
In Discussion Not yet reached consensus Suggestion An idea for TypeScript
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants