Skip to content

Fix JSDoc @param qualified name error: produce TS2749 instead of TS2503#3706

Draft
Copilot wants to merge 3 commits intomainfrom
copilot/fix-jsdoc-param-behavior-difference
Draft

Fix JSDoc @param qualified name error: produce TS2749 instead of TS2503#3706
Copilot wants to merge 3 commits intomainfrom
copilot/fix-jsdoc-param-behavior-difference

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 4, 2026

In JS files, JSDoc type references using qualified names (e.g. @param {map.foo}) where the left side is a value produced TS2503 "Cannot find namespace 'map'" instead of the more accurate TS2749 "'map.foo' refers to a value, but is being used as a type here. Did you mean 'typeof map.foo'?"

export const map = /** @type {const} */ ({
    foo: 'foo',
    bar: 'bar',
});

/**
 * @param {map.foo} foo  // tsgo: TS2503, tsc: TS2749
 */
export function buzz(foo) {}

Changes

  • resolveEntityName: Compute namespaceMeaning = Namespace | (isInJSFile ? meaning & Value : 0) matching TypeScript's behavior, and pass it to resolveQualifiedName
  • resolveQualifiedName: Two-step left-side resolution — first try Namespace-only silently, then fall back to namespaceMeaning (including Value for JS files). If both fail, re-resolve with Namespace to emit the original error
  • getTypeFromTypeReference: For JSDoc qualified type references, add two-pass resolution (first Type with ignoreErrors, then Type|Value) to enable the canSuggestTypeof error path in resolveQualifiedName
  • isJSDocTypeReference / hasQualifiedTypeName: Helper methods to scope the two-pass to only qualified names, avoiding interference with simple identifier resolution (which lacks getTypeFromJSDocValueReference support)

Baseline improvements

~20 existing tests improved from unhelpful TS2503 "Cannot find namespace" to accurate TS2749/TS2694 errors. One test (typeLookupInIIFE) now matches tsc exactly (diff file removed). One test (typeFromPropertyAssignment5) now produces no errors, matching tsc.

…d of TS2503

In JS files, when a JSDoc type reference uses a qualified name (e.g. map.foo)
where the left side is a value, tsgo was producing the unhelpful TS2503
"Cannot find namespace 'map'" error. TypeScript produces the more accurate
TS2749 "'map.foo' refers to a value, but is being used as a type here".

The fix adds two mechanisms:
1. In resolveEntityName, compute namespaceMeaning that includes Value for JS
   files (matching TypeScript's behavior).
2. In resolveQualifiedName, try resolving the left side with just Namespace
   first, then fall back to namespaceMeaning (including Value) for JS files.
3. In getTypeFromTypeReference, add a two-pass resolution for JSDoc qualified
   type references: first try Type-only, then fall back to Type|Value to
   enable better error reporting.

Agent-Logs-Url: https://github.com/microsoft/typescript-go/sessions/be55bf87-9a50-4982-84e1-d49281ca994d

Co-authored-by: DanielRosenwasser <972891+DanielRosenwasser@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix behavior difference in JSDoc @param referencing object attribute Fix JSDoc @param qualified name error: produce TS2749 instead of TS2503 May 4, 2026
Copilot AI requested a review from DanielRosenwasser May 4, 2026 22:53
Copy link
Copy Markdown
Member

@DanielRosenwasser DanielRosenwasser left a comment

Choose a reason for hiding this comment

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

@copilot maybe there is a misunderstanding. We do not want to go back to understanding obj.foo in type position as typeof obj.foo. But we do want to issue the error message that TypeScript 6 reports;

'obj.foo' refers to a value, but is being used as a type here. Did you mean 'typeof obj.foo'?

Comment thread internal/checker/checker.go Outdated

func (c *Checker) hasQualifiedTypeName(node *ast.Node) bool {
name := getTypeReferenceName(node)
return name != nil && (name.Kind == ast.KindQualifiedName || name.Kind == ast.KindPropertyAccessExpression)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do we ever actually parse as a property access? Seems like an unnecessary check.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You're right — JSDoc TypeReference nodes always have a TypeName that's either an Identifier or QualifiedName, never a PropertyAccessExpression. Removed in 40a196a.

…Name

JSDoc TypeReference nodes always have a TypeName that is either an
Identifier or QualifiedName, never a PropertyAccessExpression.

Agent-Logs-Url: https://github.com/microsoft/typescript-go/sessions/63de4b7d-b3a4-4a74-884c-d1f70828a618

Co-authored-by: DanielRosenwasser <972891+DanielRosenwasser@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented May 4, 2026

@copilot maybe there is a misunderstanding. We do not want to go back to understanding obj.foo in type position as typeof obj.foo. But we do want to issue the error message that TypeScript 6 repor...

Understood — this PR only changes the error message from TS2503 to TS2749. It does not resolve obj.foo as typeof obj.foo in type position. The parameter type remains error (shown as map.foo in baselines), not the resolved value type.

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.

Behavior difference: jsdoc @param referencing object attribute value throws TS2749 in tsc but throws TS2503 in tsgo

2 participants