Skip to content

fix(63726): fix declaration emit for multiline jsdoc literal types - #4839

Open
Oleksandr Tarasiuk (a-tarasyuk) wants to merge 2 commits into
microsoft:mainfrom
a-tarasyuk:fix/63726
Open

fix(63726): fix declaration emit for multiline jsdoc literal types#4839
Oleksandr Tarasiuk (a-tarasyuk) wants to merge 2 commits into
microsoft:mainfrom
a-tarasyuk:fix/63726

Conversation

@a-tarasyuk

Copy link
Copy Markdown
Contributor

Copilot AI balanced review requested due to automatic review settings August 6, 2026 17:20

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

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Adds a regression test and adjusts scanner trivia-skipping to correctly preserve multiline JSDoc literal union types during declaration emit (fixes TypeScript issue #63726).

Changes:

  • Added a new compiler test case for multiline JSDoc union literal typedefs in JS files.
  • Updated the reference baseline to validate correct .d.ts output and preserved JSDoc formatting.
  • Adjusted GetTextOfNodeFromSourceText to use JSDoc-aware trivia skipping when extracting text from JSDoc nodes.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
testdata/tests/cases/compiler/jsdocMultilineUnion.ts New regression test covering multiline JSDoc literal union typedef formatting.
testdata/baselines/reference/compiler/jsdocMultilineUnion.js Baseline validating correct declaration emit and preserved multiline JSDoc formatting.
internal/scanner/utilities.go Uses JSDoc-aware trivia skipping when extracting node text for JSDoc nodes.

Comment thread internal/scanner/utilities.go Outdated
Comment thread internal/scanner/utilities.go Outdated
Comment thread testdata/tests/cases/compiler/jsdocMultilineUnion.ts

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

Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.

Suppressed comments (1)

internal/scanner/utilities.go:33

  • SkipTriviaEx only removes a JSDoc asterisk while locating the node start; it does not remove prefixes inside a node's span. JSDoc types accept multiline template literals (parser.go:2823), so a type such as @typedef {`a\n * b`} T still emits the embedded *. Normalize every extracted line for descendants of JSDocTypeExpression (as the reference implementation does in utilities.ts:1290-1330) and cover a multiline template literal.
		pos = SkipTriviaEx(sourceText, pos, &SkipTriviaOptions{
			InJSDoc: node.Flags&ast.NodeFlagsJSDoc != 0,
		})

@jakebailey

Copy link
Copy Markdown
Member

The suppressed comment actually seems possibly true, have not checked.

@jakebailey

Copy link
Copy Markdown
Member

Checked, and what it noted is already a 6.0 problem, so nothing new

@jakebailey

Copy link
Copy Markdown
Member

Though, perhaps worth fixing:

diff --git a/internal/scanner/utilities.go b/internal/scanner/utilities.go
index d62247c6e4..096583636f 100644
--- a/internal/scanner/utilities.go
+++ b/internal/scanner/utilities.go
@@ -8,6 +8,7 @@ import (
 	"github.com/microsoft/typescript-go/internal/ast"
 	"github.com/microsoft/typescript-go/internal/core"
 	"github.com/microsoft/typescript-go/internal/debug"
+	"github.com/microsoft/typescript-go/internal/stringutil"
 )
 
 func tokenIsIdentifierOrKeyword(token ast.Kind) bool {
@@ -22,6 +23,32 @@ func GetSourceTextOfNodeFromSourceFile(sourceFile *ast.SourceFile, node *ast.Nod
 	return GetTextOfNodeFromSourceText(sourceFile.Text(), node, includeTrivia)
 }
 
+func isJSDocTypeExpressionOrChild(node *ast.Node) bool {
+	if node.Flags&ast.NodeFlagsJSDoc == 0 {
+		return false
+	}
+	for current := node; current != nil; current = current.Parent {
+		if ast.IsJSDocTypeExpression(current) || ast.IsTypeNode(current) {
+			return true
+		}
+	}
+	return false
+}
+
+func stripJSDocTypePrefixes(text string) string {
+	text = strings.ReplaceAll(text, "\r\n", "\n")
+	text = strings.ReplaceAll(text, "\r", "\n")
+	lines := strings.Split(text, "\n")
+	for i, line := range lines {
+		line = strings.TrimLeftFunc(line, stringutil.IsWhiteSpaceLike)
+		if strings.HasPrefix(line, "*") {
+			line = strings.TrimLeftFunc(line[1:], stringutil.IsWhiteSpaceLike)
+		}
+		lines[i] = line
+	}
+	return strings.Join(lines, "\n")
+}
+
 func GetTextOfNodeFromSourceText(sourceText string, node *ast.Node, includeTrivia bool) string {
 	if ast.NodeIsMissing(node) {
 		return ""
@@ -33,6 +60,9 @@ func GetTextOfNodeFromSourceText(sourceText string, node *ast.Node, includeTrivi
 		})
 	}
 	text := sourceText[pos:node.End()]
+	if isJSDocTypeExpressionOrChild(node) {
+		text = stripJSDocTypePrefixes(text)
+	}
 	if node.Flags&ast.NodeFlagsReparserTransformedLiteral != 0 {
 		// This is similar to `getLiteralTextOfNode` in the printer, but without the context of an `emitContext` to provide overrides
 		if ast.IsStringLiteral(node) {
@@ -48,10 +78,6 @@ func GetTextOfNodeFromSourceText(sourceText string, node *ast.Node, includeTrivi
 		// Fail on any other kinds.
 		debug.FailBadSyntaxKind(node, "Unexpected reparser-transformed node kind")
 	}
-	// if (isJSDocTypeExpressionOrChild(node)) {
-	//     // strip space + asterisk at line start
-	//     text = text.split(/\r\n|\n|\r/).map(line => line.replace(/^\s*\*/, "").trimStart()).join("\n");
-	// }
 	return text
 }

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Poorly formed output with JSDoc typedef

4 participants