Sitelet https://github.com/microsoft/TypeScript/pull/64004
Skip to content

Fix JSDoc @enum so the tagged name is a type as well as a value - #64004

Closed
Xia Chao (bun-unsafe) wants to merge 1 commit into
microsoft:mainfrom
bun-unsafe:fix/jsdoc-enum-tag
Closed

Fix JSDoc @enum so the tagged name is a type as well as a value#64004
Xia Chao (bun-unsafe) wants to merge 1 commit into
microsoft:mainfrom
bun-unsafe:fix/jsdoc-enum-tag

Conversation

@bun-unsafe

@bun-unsafe Xia Chao (bun-unsafe) commented Aug 25, 2026

Copy link
Copy Markdown

Fixes #64003

JSDoc @enum on a const object did not introduce a type. The name was value-only, so @param {E} in the same file and const n: E = E.A against the emitted .d.ts both reported TS2749.

This parses @enum, reparses a type alias named after the host declaration, and merges it with the value. Declaration emit then includes export type E = … next to the existing const. JSDoc function(number): number is parsed as a function type so @enum {function(number): number} prints a valid signature (this also clears the old parse errors on {function(string): boolean} in typedefTagWrapping).

Emit is export type E = T plus export declare const E: { … }, not the 6.x namespace shape. The type+value contract is what consumers need (E in type position, E.A as a value).

Tests: jsDeclarationsEnumTag, enumTag*, jsFileESModuleWithEnumTag, typedefTagWrapping, smartSelection_JSDocTags9.

I used Copilot while writing this patch. I read the change, ran the tests above, and I will handle review myself.

Copilot AI balanced review requested due to automatic review settings August 25, 2026 14:07
@github-project-automation github-project-automation Bot moved this to Not started in PR Backlog Aug 25, 2026
@enum was parsed as an unknown tag, so names like E were value-only.
That made @PARAM {E} and .d.ts consumers report TS2749. Parse the tag,
reparse a type alias from the host name, and parse JSDoc function(...)
types so @enum {function(number): number} emits a valid signature.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class JSDoc @enum type support and Closure-style function type parsing.

Changes:

  • Adds JSDocEnumTag AST, parser, encoder, checker, and emit support.
  • Synthesizes type aliases alongside enum values.
  • Updates compiler and fourslash baselines.

Reviewed changes

Copilot reviewed 28 out of 33 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tsc/internal/parser/jsdoc.go Parses enum tags and function types.
tsc/internal/parser/parser.go Recognizes JSDoc function(...) types.
tsc/internal/parser/reparser.go Synthesizes enum type aliases.
tsc/internal/checker/checker.go Classifies enum tags in type space.
tsc/internal/checker/emitresolver.go Handles enum-tag visibility.
tsc/internal/ast/ast.go Integrates enum-tag accessors.
tsc/internal/ast/ast_generated.go Adds generated enum-tag nodes.
tsc/internal/ast/kind_generated.go Adds the enum-tag syntax kind.
tsc/internal/ast/kind_stringer_generated.go Updates generated kind strings.
tsc/internal/ast/utilities.go Enables enum-tag comments.
tsc/internal/api/encoder/encoder_generated.go Encodes enum-tag children.
tsc/internal/api/encoder/decoder_generated.go Decodes enum-tag children.
tools/scripts/tsc/ast.json Defines the enum-tag AST schema.
smartSelection_JSDocTags9.baseline Updates JSDoc selection output.
typedefTagWrapping.types Records parsed function return types.
typedefTagWrapping.errors.txt Removes obsolete parse errors.
jsDeclarationsEnumTag(target=es2015).types Records enum aliases’ resolved types.
jsDeclarationsEnumTag(target=es2015).symbols Records merged symbols.
jsDeclarationsEnumTag(target=es2015).js Records emitted type aliases.
jsDeclarationsEnumTag(target=es2015).errors.txt Removes value-only type errors.
enumTagUseBeforeDefCrash.types Records the enum’s aliased type.
enumTagUseBeforeDefCrash.symbols Records its merged symbol.
enumTagUseBeforeDefCrash.errors.txt Removes the former type error.
enumTagOnExports.symbols Records CommonJS merged symbols.
enumTagImported.types Records imported enum types.
enumTagImported.symbols Records imported merged symbols.
enumTagImported.errors.txt Removes import/type errors.
enumTagCircularReference.symbols Records the circular alias symbol.
enumTagCircularReference.errors.txt Adds the circular-alias diagnostic.
enumTag.types Records local enum alias types.
enumTag.symbols Records local merged symbols.
enumTag.errors.txt Removes value-only type errors.
jsFileESModuleWithEnumTag.symbols Records merged ES-module symbols.
Files not reviewed (5)
  • tsc/internal/api/encoder/decoder_generated.go: Generated file
  • tsc/internal/api/encoder/encoder_generated.go: Generated file
  • tsc/internal/ast/ast_generated.go: Generated file
  • tsc/internal/ast/kind_generated.go: Generated file
  • tsc/internal/ast/kind_stringer_generated.go: Generated file

💡 Add a code-review agent skill for context-aware, tailored reviews. Learn more in the docs.

Comment thread tsc/internal/parser/jsdoc.go
Comment on lines +752 to +755
case ast.KindElementAccessExpression:
arg := expr.AsElementAccessExpression().ArgumentExpression
if ast.IsIdentifier(arg) {
return arg

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. The identifier check matches Strada nameForNamelessJSDocTypedef (element access only when the argument is an identifier).
Static exports["E"] / numeric literals are a reasonable follow-up (GetElementOrPropertyAccessName + identifier). I left that out of this PR so the @enum const/export case stays reviewable.

Comment thread tsc/internal/parser/jsdoc.go
Comment on lines +749 to +751
switch expr.Kind {
case ast.KindPropertyAccessExpression:
return expr.AsPropertyAccessExpression().Name()

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. This is a real remaining @enum hole: we only take the innermost name, so Host.UserMetrics.Action does not become a qualified type. jsEnumCrossFileExport.ts still reports TS2749.
This PR is scoped to a declaration-named const/export (export const E / .d.ts consumers). Wrapping the full static path like dotted @typedef is a follow-up. I can do it in this PR if you want it here.

@bun-unsafe

Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

@jakebailey

Copy link
Copy Markdown
Member

Everything you're adding back was intentionally removed

https://github.com/microsoft/TypeScript/blob/main/tsc/CHANGES.md

@bun-unsafe

Copy link
Copy Markdown
Author

You're right — I missed that @enum and Closure function(...) types are intentional Corsa removals, with @typedef / (s: T) => R as the substitute.

I'll close this PR. Sorry for the noise.

@github-project-automation github-project-automation Bot moved this from Not started to Done in PR Backlog Aug 25, 2026
@jakebailey

Copy link
Copy Markdown
Member

Why did you choose to work on this? Does this personally affect you?

@bun-unsafe

Copy link
Copy Markdown
Author

I was comparing 6.x vs 7 declaration emit on a small JSDoc fixture (@enum on a const, checkJs + declaration). The .d.ts left E value-only, so const n: E = E.A was TS2749, which I treated as a regression.
It does not affect a product I ship. I should have read CHANGES.md first — @enum / Closure function(...) are intentional removals, not a bug. Sorry for the noise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

JSDoc @enum does not create a type (TS2749 on .d.ts consumers)

3 participants