Skip to content

fix(65020): recover nested JSDoc @property after unparseable type - #65029

Closed
Timur Vural (timurzvural) wants to merge 1 commit into
microsoft:mainfrom
timurzvural:fix/65020-jsdoc-nested-property-recovery
Closed

Timur Vural (timurzvural) wants to merge 1 commit into
microsoft:mainfrom
timurzvural:fix/65020-jsdoc-nested-property-recovery

Conversation

@timurzvural

Copy link
Copy Markdown

Bug

When a nested JSDoc @property has a type that does not parse (commonly a removed Closure function(string)= form), every nested @property after it is dropped from the typedef. Flat properties recover; nested ones do not. This worked in 6.0.3 and broke in 7.x after Closure function(...) types were removed and recovery became insufficient.

/**
 * @typedef Sig
 * @property {object} Args
 * @property {string} Args.name
 * @property {function(string)=} Args.onSave
 * @property {((v: string) => void)=} Args.onCancel  // dropped today
 */

Root cause

  1. After an unsupported braced type like {function(string)=}, the parser treated function as a name and left (...) before }, so the property name never parsed.
  2. In parseChildParameterOrPropertyTag, a child whose name did not match (including a missing name) aborted the nested type literal, so later matching nested tags were never collected.

Fix

  • parseJSDocTypeExpressionCloseBrace: if } is missing and the next token is (, skip leftovers (with brace/paren/bracket depth) through the closing } so the tag name still parses. Only start this recovery at ( so unrelated missing-} cases keep the following name in place.
  • In parseChildParameterOrPropertyTag, keep a missing-name property/parameter child nested instead of treating it as end-of-nesting.

Tests

  • Parser unit test TestJSDocNestedPropertyRecoveryAfterBadType asserting nested Args.onCancel is kept
  • Compiler case jsdocNestedPropertyRecoveryAfterBadType.ts (+ baselines): only TS1005 on the Closure type; check.ts has 0 errors and onCancel remains in keyof Sig['Args']
  • Updated typedefTagWrapping error/types baselines where function(...) typedef names are now recovered (typed as Function)

Fixes #65020

Related: similar open #65022

AI assistance disclosure

This PR was authored with assistance from Cursor Agent. I have read and understand the change and am prepared to discuss and revise it in review.

When a braced JSDoc type fails to parse (e.g. removed Closure
function(...) syntax), skip leftover tokens through the closing }
so the property name still parses. That keeps later nested
@Property tags under the same object instead of aborting the
nested type literal. Also treat a missing nested property name as
a recoverable child rather than end-of-nesting.

Adds a compiler regression for microsoft#65020 and a parser unit test.
Updates typedefTagWrapping baselines where function(...) typedef
names are now recovered (typed as Function).

AI assistance disclosure: authored with Cursor Agent.
Copilot AI balanced review requested due to automatic review settings October 29, 2026 20:41
@github-project-automation github-project-automation Bot moved this to Not started in PR Backlog Oct 29, 2026
@typescript-automation typescript-automation Bot added the For Uncommitted Bug PR for untriaged, rejected, closed or missing bug label Oct 29, 2026
@timurzvural

Copy link
Copy Markdown
Author

Closing: #65020 is already closed, so this PR is being withdrawn.

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.

🟡 Changes recommended

JSDoc recovery can misinterpret quoted literals, skip subsequent tags, and incorrectly nest valid unnamed parameters, producing incorrect properties and parameter types.

3 open issues
What changed in this PR

This PR addresses #65020 by improving JSDoc error recovery in the native TypeScript parser so an unsupported type does not discard later nested properties.

Changes:

  • Adds recovery for leftover parenthesized type syntax and missing child-tag names.
  • Adds parser and compiler regression tests for nested property preservation.
  • Updates baselines to reflect recovered property and typedef names while retaining the syntax error.
File Description
tsc/​testdata/​tests/​cases/​compiler/​jsdocNestedPropertyRecoveryAfterBadType.ts Checks that nested property names remain available through keyof.
tsc/​testdata/​baselines/​reference/​conformance/​typedefTagWrapping.types Records recovered typedefs resolving to Function.
tsc/​testdata/​baselines/​reference/​conformance/​typedefTagWrapping.errors.txt Removes cascading errors for recovered typedef names.
tsc/​testdata/​baselines/​reference/​compiler/​jsdocNestedPropertyRecoveryAfterBadType.types Records preserved nested property keys.
tsc/​testdata/​baselines/​reference/​compiler/​jsdocNestedPropertyRecoveryAfterBadType.symbols Adds symbol expectations for the regression case.
tsc/​testdata/​baselines/​reference/​compiler/​jsdocNestedPropertyRecoveryAfterBadType.errors.txt Expects only the unsupported-type syntax error.
tsc/​internal/​parser/​parser_test.go Adds an assertion that later nested properties survive parsing.
tsc/​internal/​parser/​jsdoc.go Implements type-expression and missing-name recovery.

Review effort: Balanced

Comment on lines +149 to +151
case ast.KindOpenBraceToken, ast.KindOpenParenToken, ast.KindOpenBracketToken:
depth++
p.nextTokenJSDoc()
Comment on lines +139 to +160
depth := 0
for p.token != ast.KindAtToken && p.token != ast.KindEndOfFile {
switch p.token {
case ast.KindCloseBraceToken:
if depth == 0 {
p.nextTokenJSDoc()
return
}
depth--
p.nextTokenJSDoc()
case ast.KindOpenBraceToken, ast.KindOpenParenToken, ast.KindOpenBracketToken:
depth++
p.nextTokenJSDoc()
case ast.KindCloseParenToken, ast.KindCloseBracketToken:
if depth > 0 {
depth--
}
p.nextTokenJSDoc()
default:
p.nextTokenJSDoc()
}
}
Comment on lines +1241 to +1242
if !ast.NodeIsMissing(childName) &&
(ast.IsIdentifier(childName) || !textsEqual(name, childName.AsQualifiedName().Left)) {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

For Uncommitted Bug PR for untriaged, rejected, closed or missing bug

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

JSDoc: a nested @property with a type that does not parse drops every nested @property after it

2 participants