-
Notifications
You must be signed in to change notification settings - Fork 682
Full-signature types for JSDoc #1468
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
Conversation
…s a returning Strada feature, but it's no longer implemented as a fallback lookup for a type tag. Instead, `FunctionLikeBase` now has a property `WholeType` that gets set in the reparser. If we can come up with Typescript syntax for this feature, it will work immediately--with a couple of caveats described below. In the checker, the implementation is basically the same as in Strada, except that it refers to a property on FunctionLikeBase instead of looking up a tag. I put it on FunctionLikeBase because it is more convenient to retrieve, even though it only applies to FunctionLikeWithBodyBase. Notably, there was one check that I skipped because it short-circuits contextual typing in favour of the whole type, which I don't believe is correct--or is perhaps unobservable. When I removed it no tests changed. I didn't check that declaration emit works, but I think there may be some tests that exercise it. In any case, since the signature's types are pulled from the whole-type when needed, and parameter/return types are supposed to be nil when it's present, I expect that emit will generate types correctly from the signature. Another difference is that the parameter names of the resulting signature come from the type annotation, not the function declaration. This is consistent with something like ```ts const x: number = 'xx' ``` where `x: number` when used even though it is initialised with a string, which of course is a string. Speaking of errors, I improved the wording of the existing error and added another that forbids using `@type` and `@param/@return`. Both errors are worded in terms of JSDoc tags even though we could implement this feature in TS. We can revisit this either after coming up with a good name for this feature, or after we implement it in TS, if ever. \#\# Open Questions 1. What should this feature be called? 2. Where should the code live? 3. Can the errors be improved further? 4. What would TS syntax for this look like?
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.
Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.
testdata/baselines/reference/submodule/conformance/typeTagOnFunctionReferencesGeneric.js.diff
Outdated
Show resolved
Hide resolved
-!!! error TS2345: Type 'undefined' is not assignable to type 'number'. | ||
self(""); | ||
+ ~~~~ | ||
+!!! error TS2554: Expected 2 arguments, but got 1. |
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.
you can no longer make a parameter from a whole-type annotation optional by tacking on a nested /** @param [b] */
, sorry, it's a horrible idea that should never have worked.
testdata/baselines/reference/submoduleAccepted/conformance/checkJsdocTypeTag6.errors.txt.diff
Outdated
Show resolved
Hide resolved
@@ -14,8 +14,8 @@ | |||
const tseslint = require('./typescript-eslint.js'); | |||
->tseslint : typeof tseslint | |||
->require('./typescript-eslint.js') : typeof tseslint | |||
+>tseslint : { config: (...configs: any[]) => void; } | |||
+>require('./typescript-eslint.js') : { config: (...configs: any[]) => void; } | |||
+>tseslint : { config: (...configs: import("./typescript-eslint.js").Config[]) => void; } |
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.
we now dereference the imported type instead of leaving it as typeof eslint
.
...lines/reference/submoduleAccepted/conformance/asyncFunctionDeclaration16_es5.errors.txt.diff
Outdated
Show resolved
Hide resolved
testdata/baselines/reference/submoduleAccepted/conformance/checkJsdocTypeTag6.types.diff
Outdated
Show resolved
Hide resolved
...aselines/reference/submoduleAccepted/conformance/jsdocFunction_missingReturn.errors.txt.diff
Show resolved
Hide resolved
+ ~~ | ||
+!!! error TS2702: 'NS' only refers to a type, but is being used as a namespace here. | ||
+ ~~~~~~~~~~~~~~~ | ||
+!!! error TS8030: A JSDoc '@type' tag on a function must have a signature with the correct number of arguments. |
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.
our fancy span for the first error hurts us because the useless second error would get deduped if it used the whole span of the name.
testdata/baselines/reference/submoduleAccepted/conformance/checkJsdocTypeTag6.errors.txt.diff
Show resolved
Hide resolved
.../baselines/reference/submoduleAccepted/conformance/errorOnFunctionReturnType.errors.txt.diff
Outdated
Show resolved
Hide resolved
1. Improve error spans on new/updated errors. 2. Improve check for new/updated errors. 3. Add (a lot of) code to emit type parameters in d.ts. I'm not at all fluent with the declaration emitter code, so I need careful review of it.
Thanks unified.js! Most complicated JSDoc I have ever seen.
I love this and i would love to have the same in TS. Could this be a potential syntax? function my_function(){
} typed MyFunction; this would require a new construct but it seems very legible |
.../baselines/reference/submoduleAccepted/conformance/errorOnFunctionReturnType.errors.txt.diff
Show resolved
Hide resolved
Other than the one error positioning thing, I think this seems good. The CHANGES.md file also needs an update, but I think that PR was never merged so that's not in this PR. |
Yeah, I got sidetracked from updating CHANGES.md. |
@paoloricciuti I'm afraid that would be ambiguous with existing expression syntax. Really what we should do is start a new issue to track this idea, but it sounds like the team has tried to come up with ideas in the past and failed to find any that more than 1 person likes. |
I renamed WholeType to FullSignature based on ideas from the design meeting yesterday, so I think this is ready for final review. I decided to keep the new error span since it's closer to the intent of Typescript's span. Also I didn't want to write any more special-case code for functions (which may be how the TS code was arrived at as well) |
Can you explain what you mean by this? I'm surprised by the current location just because the error goes on the type and not on the thing causing the error. It feel like if we had a function returning the wrong type, so put the error on the return type instead of the returned expression. |
Yes. The Typescript code is |
I think you might misunderstand my example? I'm comparing this PR's behavior to if you wrote: function foo(): number {
return "Hello!";
} And the error were placed on I suppose if this PR had "real syntax", like: function foo() :: Hmm {
return "Hello!";
} And the func were missing a parameter /** @type {Hmm} */
function foo() {
return "Hello!";
} And have the error appear on |
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.
Implementation wise, this seems fine, though.
The only other thing I was wondering is if we really need this on FunctionLikeData and not just a specific node or two (does this actually work on accessors? Is it tested?), but it's probably okay.
it is supposed to work on accessors; I don't know if we have test coverage for it. |
This PR allows
@type
tags to apply as a whole to a function. This is a returning Strada feature, but it's no longer implemented as a fallback lookup for a type tag. Instead,FunctionLikeBase
now has a propertyWholeType
that gets set in the reparser. If we can come up with Typescript syntax for this feature, it will work immediately--with a couple of caveats described below.In the checker, the implementation is basically the same as in Strada, except that it refers to a property on FunctionLikeBase instead of looking up a tag. I put it on FunctionLikeBase because it is more convenient to retrieve, even though it only applies to FunctionLikeWithBodyBase. Notably, there was one check that I skipped because it short-circuits contextual typing in favour of the whole type, which I don't believe is correct--or is perhaps unobservable. When I removed it no tests changed.
I didn't check that declaration emit works, but I think there may be some tests that exercise it. In any case, since the signature's types are pulled from the whole-type when needed, and parameter/return types are supposed to be nil when it's present, I expect that emit will generate types correctly from the signature.
Another difference is that the parameter names of the resulting signature come from the type annotation, not the function declaration. This is consistent with something like
where
x: number
when used even though it is initialised with a string, which of course is a string.Speaking of errors, I improved the wording of the existing error and added another that forbids using
@type
and@param/@return
. Both errors are worded in terms of JSDoc tags even though we could implement this feature in TS. We can revisit this either after coming up with a good name for this feature, or after we implement it in TS, if ever.Open Questions
Open Tasks