-
Notifications
You must be signed in to change notification settings - Fork 12.5k
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
JSDoc overload tag #51234
JSDoc overload tag #51234
Conversation
@microsoft-github-policy-service agree |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks great. Clever re-use of existing infrastructure for @callback
. Just a few questions and requests for additional tests. I also want to think about the design a bit more, but so far I think it's solid.
=== tests/cases/compiler/jsFileFunctionOverloads.js === | ||
/** | ||
* @overload | ||
* @param {number} x |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what happens if you find-all-ref or rename the overload parameters. In Typescript, they are not connected to the implementation signatures and don't rename.
Can you add two tests in cases/fourslash, one that renames an overload param and one that find-all-refs it, and shows that overload params are not connected to the implementation? There should be existing tests for @param
that you can start from.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I only tried this in VSCode, and it seems like your assumptions are correct, i.e. the overload parameters are generally ignored on both find-all-ref
and rename
operations.
I would be happy to add those tests, but can you please point me to an example where a similar test was already implemented? I am fairly new to this codebase and navigating around (especially within tests) is still challenging 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added tests for find-all-ref
and rename
as requested.
src/compiler/checker.ts
Outdated
if (node.tags) { | ||
for (const tag of node.tags) { | ||
if (isJSDocOverloadTag(tag)) { | ||
result.push(getSignatureFromDeclaration(tag.typeExpression)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Operations that go from a signature back to a declaration might fail if the signature is a JSDocSignature. But (1) JSDocCallbackTag already exists, and has shaken out those bugs. (2) I can't think of any specific places; the only new ones would be ones that operate on symbols with multiple signatures.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there anything in particular here that you would like me to look into?
*/ | ||
/** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what happens if all the @overload
tags are in one comment? Does that work too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, actually it does. At least the examples I checked locally seem to work properly. Do you think we should be adding this as a test case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just in case, I've also added another variant of tests, which tries to put all @overload
tags in a single comment. It seems like things get tricky (unsurprisingly) if you also want to use @template
tag, as you can only have a single "scope" for template parameters per JSDoc comment. This problem is not specific to @overload
tag though, and I think it should be good enough for now if it's covered in documentation properly. Perhaps this could be improved in the future if we allow @template
to be parsed as part of JSDocSignature
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I think it's good enough for now.
|
||
/** | ||
* @overload | ||
* @this {Example<'number'>} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see this
in the type. Does this actually work?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for pointing this out! I forgot that JSDocSignature
does not support @this
tag, but it seems like @param {...} this
works well. I've just pushed a fix.
I did a quick grep of JS code (all the JS that gets installed when installing the dependencies of every package on Definitely Typed). This found one existing use of Here are the interesting cases I've found so far: callable constructor function /**
* @overload Endpoint(endpoint)
* Constructs a new endpoint given an endpoint URL. If the
* URL omits a protocol (http or https), the default protocol
* set in the global {AWS.config} will be used.
* @param endpoint [String] the URL to construct an endpoint from
*/
constructor: function Endpoint(endpoint, config) { callable constructor function with overloadsNote that the second overload has zero param tags, just /**
* A credentials object can be created using positional arguments or an options
* hash.
*
* @overload AWS.Credentials(accessKeyId, secretAccessKey, sessionToken=null)
* Creates a Credentials object with a given set of credential information
* as positional arguments.
* @param accessKeyId [String] the AWS access key ID
* @param secretAccessKey [String] the AWS secret access key
* @param sessionToken [String] the optional AWS session token
* @example Create a credentials object with AWS credentials
* var creds = new AWS.Credentials('akid', 'secret', 'session');
* @overload AWS.Credentials(options)
* Creates a Credentials object with a given set of credential information
* as an options hash.
* @option options accessKeyId [String] the AWS access key ID
* @option options secretAccessKey [String] the AWS secret access key
* @option options sessionToken [String] the optional AWS session token
* @example Create a credentials object with AWS credentials
* var creds = new AWS.Credentials({
* accessKeyId: 'akid', secretAccessKey: 'secret', sessionToken: 'session'
* });
*/
constructor: function Credentials() { The associated hand-written overload set: constructor(options: CredentialsOptions);
constructor(accessKeyId: string, secretAccessKey: string, sessionToken?: string); single signature with split return typesThere's a lot going on here that would be good to test:
Again, this doesn't need to have the correct semantics, it just needs to not crash.
Here's the hand-created .d.ts overload set for comparison: getSynthesizeSpeechUrl(params: Polly.Types.SynthesizeSpeechInput, error: number, callback: (err: AWSError, url: string) => void): void;
getSynthesizeSpeechUrl(params: Polly.Types.SynthesizeSpeechInput, callback: (err: AWSError, url: string) => void): void;
getSynthesizeSpeechUrl(params: Polly.Types.SynthesizeSpeechInput, expires?: number): string; |
Thank you for taking the effort to find these examples!
Sure, I will try to add the tests for the use cases that you listed. |
3d0f56b
to
f0ec97c
Compare
@sandersn I pushed some additional tests and also used this opportunity to resolve some conflicts. It would be great if you could have a second look 😄 |
// @declaration: true | ||
// @filename: jsFileMethodOverloads2.js | ||
|
||
// Also works if all @overload tags are combined in one comment. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍
|
||
/** | ||
* @overload | ||
* @param {Example<number>} this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
huh. I had no idea this worked.
@sandersn Thank you for the review and for the positive feedback! |
Also, make @overload work like other jsdoc tags: only the last jsdoc tag before a declaration is used. That means all overload tags need to be in one tag: ```js /** * @overload * @return {number} * * @overload * @return {string} */ ``` function f() { return "1" } This no longer works: ```js /** * @overload * @return {number} */ /** * @overload * @return {string} */ function f() { return "2" } ``` Fixes microsoft#51234
Fixes #25590
This PR replaces an earlier attempt: #50789
Since there was no final consensus, I ended up using a new @overload tag to ensure backwards compatibility, e.g.
I would be happy to modify the implementation if someone suggests a better approach.