Skip to content

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

Open
@jakebailey

Description

@jakebailey

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    In DiscussionNot yet reached consensusSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions