Repository navigation
Implement erasable enum annotations - #65421
Draft
magic-akari wants to merge 4 commits into
Draft
magic-akari wants to merge 4 commits into
magic-akari wants to merge 4 commits into
Conversation
magic-akari
marked this pull request as ready for review
November 10, 2026 04:19
Contributor
|
The TypeScript team hasn't accepted the linked issue #60790. If you can get it accepted, this PR will have a better chance of being reviewed. |
Contributor
There was a problem hiding this comment.
🟡 Changes recommended
Enum literals still fail AST traversal because isExpression rejects them and incorrectly trigger an isolatedModules diagnostic for imported string initializers.
2 open issues
7 files not reviewed
tsc/internal/api/encoder/decoder_generated.go: Generated filetsc/internal/api/encoder/encoder_generated.go: Generated filetsc/internal/api/enum_values_generated.go: Generated filetsc/internal/ast/ast_generated.go: Generated filetsc/internal/ast/kind_generated.go: Generated filetsc/internal/ast/kind_stringer_generated.go: Generated filetsc/internal/diagnostics/diagnostics_generated.go: Generated file
What changed in this PR
Adds erasable : enum annotations to the native TypeScript compiler, giving object literals nominal enum types without changing their JavaScript evaluation. This implements the annotation approach discussed in #60790 and builds on #61414.
Changes:
- Adds parsing, binding, type checking, and diagnostics for enum literals.
- Emits ordinary JavaScript objects while preserving enum annotations and member types in declarations.
- Extends AST APIs and adds regression tests and baselines.
| File | Description |
|---|---|
tsc/testdata/tests/cases/compiler/enumLiteralSimple.ts |
Tests basic enum annotations. |
tsc/testdata/tests/cases/compiler/enumLiteralModules.ts |
Tests exports, imports, and aliases. |
tsc/testdata/tests/cases/compiler/enumLiteralErrors.ts |
Tests invalid members and assignments. |
tsc/testdata/tests/cases/compiler/enumLiteralErasable.ts |
Tests erasure, narrowing, and scopes. |
tsc/testdata/tests/cases/compiler/enumLiteralDeclarations.ts |
Tests declaration visibility and aliases. |
tsc/testdata/tests/cases/compiler/enumLiteralBasics.ts |
Tests member types and assignability. |
tsc/testdata/baselines/reference/compiler/enumLiteralSimple.types |
Records basic inferred types. |
tsc/testdata/baselines/reference/compiler/enumLiteralSimple.symbols |
Records basic symbol bindings. |
tsc/testdata/baselines/reference/compiler/enumLiteralSimple.js |
Records basic JavaScript emit. |
tsc/testdata/baselines/reference/compiler/enumLiteralModules(module=esnext).types |
Records ES module types. |
tsc/testdata/baselines/reference/compiler/enumLiteralModules(module=esnext).symbols |
Records ES module bindings. |
tsc/testdata/baselines/reference/compiler/enumLiteralModules(module=esnext).js |
Records ES module and declaration emit. |
tsc/testdata/baselines/reference/compiler/enumLiteralModules(module=commonjs).types |
Records CommonJS types. |
tsc/testdata/baselines/reference/compiler/enumLiteralModules(module=commonjs).symbols |
Records CommonJS bindings. |
tsc/testdata/baselines/reference/compiler/enumLiteralModules(module=commonjs).js |
Records CommonJS and declaration emit. |
tsc/testdata/baselines/reference/compiler/enumLiteralErrors.types |
Records types for invalid cases. |
tsc/testdata/baselines/reference/compiler/enumLiteralErrors.symbols |
Records bindings for invalid cases. |
tsc/testdata/baselines/reference/compiler/enumLiteralErrors.js |
Records emit for invalid cases. |
tsc/testdata/baselines/reference/compiler/enumLiteralErrors.errors.txt |
Records expected diagnostics. |
tsc/testdata/baselines/reference/compiler/enumLiteralErasable(target=esnext).types |
Records ESNext narrowing and types. |
tsc/testdata/baselines/reference/compiler/enumLiteralErasable(target=esnext).symbols |
Records ESNext symbol bindings. |
tsc/testdata/baselines/reference/compiler/enumLiteralErasable(target=esnext).js |
Records ESNext erasure and declarations. |
tsc/testdata/baselines/reference/compiler/enumLiteralErasable(target=es2015).types |
Records ES2015 narrowing and types. |
tsc/testdata/baselines/reference/compiler/enumLiteralErasable(target=es2015).symbols |
Records ES2015 symbol bindings. |
tsc/testdata/baselines/reference/compiler/enumLiteralErasable(target=es2015).js |
Records ES2015 erasure and declarations. |
tsc/testdata/baselines/reference/compiler/enumLiteralDeclarations(module=esnext).types |
Records exported and namespace types. |
tsc/testdata/baselines/reference/compiler/enumLiteralDeclarations(module=esnext).symbols |
Records declaration-related bindings. |
tsc/testdata/baselines/reference/compiler/enumLiteralDeclarations(module=esnext).js |
Records ES module declaration visibility. |
tsc/testdata/baselines/reference/compiler/enumLiteralDeclarations(module=commonjs).types |
Records CommonJS exported types. |
tsc/testdata/baselines/reference/compiler/enumLiteralDeclarations(module=commonjs).symbols |
Records CommonJS declaration bindings. |
tsc/testdata/baselines/reference/compiler/enumLiteralDeclarations(module=commonjs).js |
Records CommonJS declaration visibility. |
tsc/testdata/baselines/reference/compiler/enumLiteralBasics.types |
Records member and assignment types. |
tsc/testdata/baselines/reference/compiler/enumLiteralBasics.symbols |
Records member symbol resolution. |
tsc/testdata/baselines/reference/compiler/enumLiteralBasics.js |
Records basic erasure output. |
tsc/testdata/baselines/reference/compiler/enumLiteralBasics.errors.txt |
Records assignment and initializer errors. |
tsc/internal/transpile/enum_literal_test.go |
Tests transpilation and declaration round trips. |
tsc/internal/transformers/tstransforms/typeeraser.go |
Converts enum literals to ordinary objects. |
tsc/internal/transformers/declarations/transform.go |
Preserves enum members in declarations. |
tsc/internal/printer/printer.go |
Prints enum literal member syntax. |
tsc/internal/printer/printer_test.go |
Tests enum literal printing. |
tsc/internal/parser/parser.go |
Parses contextual annotations and members. |
tsc/internal/parser/enum_literal_test.go |
Tests parsing, recovery, and contexts. |
tsc/internal/diagnostics/diagnostics_generated.go |
Adds the generated prototype-key diagnostic. |
tsc/internal/diagnostics/diagnosticMessages.json |
Defines the prototype-key diagnostic. |
tsc/internal/diagnostics/diagnosticMessages.generated.json |
Adds generated diagnostic text. |
tsc/internal/checker/grammarchecks.go |
Allows enum literal ambient initializers. |
tsc/internal/checker/erasable_enum_test.go |
Tests semantics and declaration conflicts. |
tsc/internal/checker/emitsupport.go |
Tracks enum literal declaration visibility. |
tsc/internal/checker/checker.go |
Adds enum literal typing and validation. |
tsc/internal/binder/referenceresolver.go |
Handles exported enum literal references. |
tsc/internal/binder/enum_literal_test.go |
Tests symbols and prohibited merging. |
tsc/internal/binder/binder.go |
Binds variables and literals as enum symbols. |
tsc/internal/ast/utilities.go |
Adds enum literal recognition helpers. |
tsc/internal/ast/precedence.go |
Classifies enum literal precedence. |
tsc/internal/ast/kind_stringer_generated.go |
Updates generated syntax-kind names. |
tsc/internal/ast/kind_generated.go |
Adds the enum literal syntax kind. |
tsc/internal/ast/deepclone_test.go |
Tests enum literal cloning. |
tsc/internal/ast/ast.go |
Adds member access and subtree facts. |
tsc/internal/ast/ast_generated.go |
Adds generated Go AST support. |
tsc/internal/api/enum_values_generated.go |
Exposes the new syntax-kind value. |
tsc/internal/api/encoder/encoder_generated.go |
Encodes enum literal children. |
tsc/internal/api/encoder/decoder_generated.go |
Decodes enum literal nodes. |
tools/scripts/tsc/ast.json |
Defines the enum literal AST schema. |
packages/typescript/src/ast/visitor.generated.ts |
Adds enum literal child traversal. |
packages/typescript/src/ast/is.generated.ts |
Adds the enum literal type guard. |
packages/typescript/src/ast/factory.generated.ts |
Adds factories, cloning, and traversal. |
packages/typescript/src/ast/ast.generated.ts |
Defines the public enum literal interface. |
packages/typescript/src/api/node/protocol.generated.ts |
Describes enum literal protocol children. |
packages/typescript/enums/syntaxKind.js |
Exposes the runtime syntax kind. |
packages/typescript/enums/syntaxKind.d.ts |
Declares the public syntax kind. |
Review effort: Balanced
magic-akari
marked this pull request as draft
November 10, 2026 04:38
magic-akari
force-pushed
the
feat/erasable-enum
branch
from
November 10, 2026 08:14
56d4c2b to
92b1155
Compare
Give object literals nominal enum types with erasable `: enum` annotations. Preserve runtime behavior and member types in declaration files.
magic-akari
force-pushed
the
feat/erasable-enum
branch
from
November 10, 2026 08:23
92b1155 to
057fd88
Compare
magic-akari
marked this pull request as ready for review
November 10, 2026 08:47
Ryan Cavanaugh (RyanCavanaugh)
marked this pull request as draft
November 10, 2026 17:53
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

The goal is to provide stronger type constraints than an ordinary object without requiring an enum-specific runtime transform. As described in #60790, the
as constobject pattern requires a verbosetypeof X[keyof typeof X]alias and produces a plain union rather than a nominal enum type.This PR adds erasable
: enumannotations, combining ordinary JavaScript object literals with enum types and named member types:The implementation separates parsing, type checking, and emission:
: enumannotation.erasableSyntaxOnlyconstraint by construction.: enumand serialize evaluated member values into.d.tsfiles. For example,A: 1 << 2becomesA: 4only in declaration output; JavaScript retainsA: 1 << 2. UnderisolatedDeclarations, self-contained constant expressions are supported, while references to symbols outside the enum are rejected, including constants declared in the same file.For named variable statements annotated with
: enum, the checker applies the following rules. These are TypeScript semantic restrictions, not additional validation requirements for type-stripping tools.A: 1,"name": "value","1": "one"1: "one"["A"]: 1,[key]: 1A: 1 << 2,B: "a" + "b"A: n, whereconst n = 1isolatedDeclarations({ A: ((1)) })...otherAA: AA() {}get A() { return 1; },set A(v) {}A: true,A: null,A: 1n,A: Symbol(),A: {}number/stringreferencesA: Math.random(),A: nwherelet n = 1satisfies, and non-null assertionsA: 1 as number,A: 1 satisfies number,A: value!__proto__: null,"__proto__": (null)const E: enum = { A: 1, B: E.A }as enumassertion for object literals #60790