Repository: TypeScript
Issue: Solutions for Editing Issues with JSDoc in TypeScript Files
Labels: Suggestion,In Discussion,Domain: JSDoc

I've been thinking about a set of specific problems that relate to how JSDoc functions when we edit TypeScript.

# Issues with Unbound Nodes

The first problem is that within a TypeScript file, we don't bind types within JSDoc comments. As a result, later operations in the language service may jump into a type node embedded in JSDoc, assume that some anonymous types have a symbol available, and fail. Two instances of this include https://github.com/microsoft/TypeScript/issues/50750 and https://github.com/microsoft/TypeScript/issues/51054. This isn't ideal, but luckily this isn't very apparent to the user - completion might not work for that request, but the editing experience will still work.

The reason why we don't bind is probably for two reasons - the most obvious one is to avoid doing any more work than is necessary. The other (and more important) reason is that there is some default behavior of *declaration* JSDoc comments where binding them adds a type in the current scope. A type declared by a `@typedef` or a function with a `@constructor` or `@class`. This is not such a huge point of complexity. We could easily make this behavior conditional, or add a separate binding strategy for these code paths.

# Issues with Diagnostics in JSDoc

The second problem is that within a TypeScript file, we may accidentally report errors in JSDoc comments. Some instances of this include https://github.com/microsoft/TypeScript/issues/50341 and https://github.com/microsoft/TypeScript/issues/49109.

For some background knowledge on this issue, we still parse and preserve JSDoc in our TypeScript syntax trees, and plain TypeScript types can be embedded within JSDoc comments. What I believe is happening is that a user can perform editor operations in arbitrary locations, and these editor operations will perform some semantic query in the language service. If a semantic query is performed within a JSDoc comment, it will perform some type-checking which can result in new diagnostics being added to the current file. If the editor requests diagnostics *after* that query, then the inappropriate error will stick around in addition to the usual diagnostics the type-checker will pick up. This used to not be a problem because our language service spun up two type-checkers - one for type-checking, and the other for answering questions about the program; but recently we consolidated them into one. So previously, diagnostics would still be created, they were never user-visible.

# Fixing the Binding Issue

One approach to fixing the issue of unbound anonymous types is just to guard against every possible operation in the language service. While I think that that is one possible approach, it is likely to be spotty and there will always be a few bugs we technically won't catch; plus, it would be nice if the editor operations that are erroring actually did work in JSDoc.

Instead, I think we should just bind in JSDoc. We should either do this via an alternative path for binding anonymous types in JSDoc, or make our binding process more aware of how to prevent JSDoc comments from declaring types in the their containing scope in TypeScript files. The point here is to bind without creating symbols that are actually reachable from non-JSDoc TypeScript nodes. This should get rid of the weird bugs we've hit with anonymous types.

https://github.com/microsoft/TypeScript/pull/51066 already approximates this behavior and our perf suite doesn't seem to indicate any regression. It also improves some language service operations.

# Fixing the Checking Issue

What about the errors-in-JSDoc-in-TypeScript issue? I don't really see a clean way to fix this given the fact that we allow TypeScript syntax to occur within JSDoc nodes. Guarding against individual call sites feels hopeless, and on top of that, some semantic operations really should be supported in JSDoc.

The easiest way to fix this is to just discard diagnostics when they're issued on JSDoc-marked nodes. Technically an error could be issued in the checker on any error span, but it's rare, and we could add special logic in those places.

## Lemons into Lemonade?

Another idea is that instead of tossing away diagnostics from JSDoc comments, what if we actually just checked JSDoc comments? Of course, we probably don't want to break someone's build based on JSDoc comments in a `.ts` file, so instead of providing plain errors within these comments, we could provide suggestion diagnostics. This would use the same technique described above to just not error within JSDoc comments. Plus, we already do something like this for misspelled parameter names on `@param` tags. If you're referencing an entity that's misspelled in a `@link` tag, or using a type incorrectly in a JSDoc comment, it seems worthwhile to know about, right? We can surface that information in the editor, where it's shown as three gray dots today in VS Code.

This will inevitably be more work for the type-checker. It might also stress test us enough to the point where we start uncovering bugs most users would never have hit in the first place; however, we could take the same perspective with consolidating type-checkers. Instead, we acknowledged that the tail of bugs was worth the work in the first place because we were masking over correctness issues. Plus, I don't know how much more work this would add to the type-checker in practice. JSDoc comments are fairly shallow nodes, usually without much complexity in them, but "usually" can do a lot of work.