-
Notifications
You must be signed in to change notification settings - Fork 12.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Proposal for JSDoc comment support to inform a better JavaScript editing experience. #2913
Comments
looks good! |
The current plan is that this should only effect the editing experience in JavaScript files. We should consider whether we want this to work in TypeScript files as well. A couple reasons:
This does create additional complications where there are now 2 type annotations for a given element that we would want to verify and keep in sync. It's probably not an option to delete the JSDoc comments since there're plenty of good reasons to have some later portion in your pipeline still use JSDoc comments even after the TypeScript compiler is done with your file(s). |
@danquirk: Regarding the second point, if the user wants to have JSDoc annotations in their code, then perhaps a solution would be to
Then the programmer needs maintain only one copy of the annotations in the form of TS files. There remains the outstanding question of whether we might be emitting too much type information, rendering the output file bloated for the remaining tools in the pipeline. |
Pull Request here: #2646
Problem
Providing a good JavaScript editing experience in a cross-platform/cross-editor manner is challenging. JavaScript is a highly dynamic language, and users may write code in a manner that our default TypeScript language rules don't provide a good experience for.
For example, a user may write the following:
TypeScript's view of this variable is that it is a
string
(due to its initializer). Leading to an IDE experience like so:(notice the lack of
number
properties)The solution to this in TypeScript code is easy, just write your code as:
Of course, then you're not typing JavaScript anymore, defeating the purpose of providing a good JavaScript editing experience. :-)
Proposed Solution
There is, fortunately, an approach already taken by the JavaScript community to help out here. JSDoc comments support a number of mechanisms to allow declaring valuable information about their declarations. As part of our work to better support editing JavaScript using the TypeScript compiler, we can inform the typing experience based on these JSDoc comments. These comments already appear to be widely used in the JavaScript community, and there is ample tooling support for them already in many other tools.
Here's an example of how we can improve our editing scenario if we take advantage of these annotations:
If we allow these JSDoc type annotations to inform the compiler's type-system in JavaScript files, then we can see a very nice set of functionality light up immediately like so:
![image](https://cloud.githubusercontent.com/assets/4564579/7328499/efa7df82-ea8a-11e4-8eee-6cfcce1c03ee.png)(notice both the
string
andnumber
properties listed).I've gone ahead and added support for a subset of JSDoc annotations that make sense in our compiler's existing system. This is an initial starting point for JSDoc support, and I am performing continued investigations to see what other sort of JSDoc concepts are used commonly, and what we may be able to cheaply provide support for. Current support has been added for a wide variety of types that we see in JSDoc types including:
number
,string
, etc.ArrayBuffer
. When these are to types already in some .d.ts files, this is naturally understood by the compiler.Array<number>
.*
type. This is theall
type for JSDoc, and maps to ourany
type.?
type. This maps to theunknown
type.number|string
.number?
or?number
. There is no direct representation for this in our compiler, so we map this simply to the underlying element type (in this casenumber
).number!
or!number
. There is no direct representation for this in our compiler, so we map this simply to the underlying element type (in this casenumber
).{ a: number, b: string }
Additionally, based on how we've seen JSDoc used in practice on GitHub, we also support some common types like
number[]
[number,string]
On top of the types that we understand and can map to our existing TypeScript compiler types, we also understand
function
,param
andreturn
annotations. For example, a variable with an annotation of the form:Is understood to have a normal TypeScript construct signature of:
![image](https://cloud.githubusercontent.com/assets/4564579/7328590/89bfa4f6-ea8b-11e4-8a6f-e586fd93a1ff.png)
Within
function
orparam
annotations we supportfunction(new:ArrayBuffer,number)
. This represents a construct signature.function(this:ArrayBuffer,string)
. Thethis
annotation indicates the type thatthis
has within the body of the function. Interestingly enough, there is no TypeScript way to do the same thing (something we might want to reconsider).function(...number)
. This naturally maps to our own... - rest parameter
concept.function(number=)
. This naturally maps to our? - optional parameter
concept.@template T
. These naturally map to our own Type-Parameters.Here are examples of a few of these interesting cases:
Implementation
Adding support for this was fairly simple and fit nicely within our existing compiler with very little effort. The work was broken down into the following pieces:
@template
annotations and appropriately adding their Type-Parameters to the parent function. Handling record types ({ ... }
) in a similar manner to how we handle TypeScript Object-Type-Literals. And handling function typesfunction(type1, etc.)
in a similar manner to how we handle TypeScript call/construct signatures.getReturnTypeFromBody
), we check if this is a JavaScript node, and if it has an@return
annotation. If so, we defer to that as the canonical source of truth about this function's return type. After all, if this user declared it as such, then we should respect that declaration. If no such annotation can be found, we fall back to our normal inference model. The places touched in the checker are fairly minimal and reuse existing compiler functionality as much as possible.The work was done in the core compiler itself as it is the most natural place for this understanding. By doing the understanding at the compiler layer, all IDE features naturally light-up, with no additional effort. If this work was done at the Services layer it would require an enormous amount of effort. To understand this, let's work with a simple example:
If the Services layer wanted to handle this properly, it would not be sufficient to merely understand the types of
x
andy
. It would also have to understand how those types flowed and changed through arbitrary expression. This is a somewhat simple case here but would already require the Services layer to implement understanding of binary expressions. This immediately spirals into too much complexity the moment we would need to understand the gamut of other cases supported in the compiler (like overload resolution, contextual types, generics, etc.)Feedback is welcome on the current design and implementation. I will be spending the immediate present doing more investigations into how JSDoc is used in the wild to better flesh things out from here.
Thanks!
The text was updated successfully, but these errors were encountered: