-
Notifications
You must be signed in to change notification settings - Fork 12.8k
Fix js missing type arguments on existing nodes and jsdoc object literal declaration emit #38368
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
Fix js missing type arguments on existing nodes and jsdoc object literal declaration emit #38368
Conversation
…ral declaration emit
Not sure if we baseline outputs for .d.ts files but @typescript-bot test this |
Heya @DanielRosenwasser, I've started to run the parallelized community code test suite on this PR at c827007. You can monitor the build here. |
Heya @DanielRosenwasser, I've started to run the extended test suite on this PR at c827007. You can monitor the build here. |
We do not. Extended test suites are entirely useless for observing changes in jsdoc declaration emit~ |
@typescript-bot pack this |
Heya @DanielRosenwasser, I've started to run the tarball bundle task on this PR at c827007. You can monitor the build here. |
The user suite test run you requested has finished and failed. I've opened a PR with the baseline diff from master. |
@typescript-bot cherry-pick this to release-3.9 |
Heya @weswigham, I've started to run the task to cherry-pick this into |
Hey @weswigham, I've opened #38372 for you. |
Component commits: c827007 Fix js missing type arguments on existing nodes and jsdoc object literal declaration emit
@@ -5503,6 +5503,10 @@ namespace ts { | |||
return symbol.declarations && find(symbol.declarations, s => !!getEffectiveTypeAnnotationNode(s) && (!enclosingDeclaration || !!findAncestor(s, n => n === enclosingDeclaration))); | |||
} | |||
|
|||
function getExistingNodeHasNoTypeParametersOrMatchingTypeParameters(existing: TypeNode, type: Type) { | |||
return !(getObjectFlags(type) & ObjectFlags.Reference) || !isTypeReferenceNode(existing) || length(existing.typeArguments) >= getMinTypeArgumentCount((type as TypeReference).target.typeParameters); |
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 is kinda surprising to me - this checks whether the existing type has more type arguments which I'd assume to be the opposite condition.
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.
Yeah, if the existing declaration provides the minimum required or more, it's good to reuse; how's that surprising?
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.
Maybe it's a naming issue? What is the meaning of hasNoTypeParameters
in this case?
@@ -5503,6 +5503,10 @@ namespace ts { | |||
return symbol.declarations && find(symbol.declarations, s => !!getEffectiveTypeAnnotationNode(s) && (!enclosingDeclaration || !!findAncestor(s, n => n === enclosingDeclaration))); | |||
} | |||
|
|||
function getExistingNodeHasNoTypeParametersOrMatchingTypeParameters(existing: TypeNode, type: Type) { |
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.
Should this start with the word get
?
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.
By convention, yes - pretty much all of our complex internal functions start with verbs, get
, create
, serialize
, and so on.
It makes completions easier to trigger, tbh.
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 about predicate functions like typeHasCallOrConstructSignatures
? Doesn't seem appropriate to start with a get
here IMO
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.
but emacs doesn't show completions until I've typed 3 characters!
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 renamed it to existingTypeNodeIsNotReferenceOrIsReferenceWithCompatibleTypeArgumentCount
which surely isn't a mouthful. I hope the removal of the verb is to your liking?
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.
named perfectly!
Hi! @weswigham There're also some invalid JSDoc basic type generation, can you also check this out? /** @type {Boolean} */const bool = true;
/** @type {bool} */const bool2 = true;
/** @type {Object} */const obj = true;
/** @type {String} */const str = true;
/** @type {Function} */const f = true;
/** @type {function} */const f2 = true;
/** @type {class} */const clz = true;
/** @type {int} */const x3 = true;
/** @type {float} */const x4 = true;
/** @type {Number} */const x5 = true;
/** @type {integer} */const x6 = true; Code /** @type {Boolean} */ declare const bool: Boolean; // expect boolean
/** @type {bool} */ declare const bool2: bool; // expect boolean
/** @type {Object} */ declare const obj: Object; // expect object
/** @type {String} */ declare const str: String; // expect string
/** @type {Function} */ declare const f: Function; // expect (...args: any[]) => any
/** @type {function} */ declare const f2: function; // ! Invalid output ! expect (...args: any[]) => any
/** @type {class} */ declare const clz: any; // expect { new (...args: any[]): any }
/** @type {int} */ declare const x3: any; // expect number
/** @type {float} */ declare const x4: any; // expect number
/** @type {Number} */ declare const x5: Number; // expect number
/** @type {integer} */ declare const x6: any; // expect number I found this when I generate types for a real world JSDoc project, it's nice if TypeScript can handle this. |
I... Don't think we recognize |
@Jack-Works I've added an explicit testcase for our declaration emit for all the special lookups (as enumerated in our codebase), and included extra entries for the types you mention which we don't currently recognize, including |
/** @type {function} */ declare const h: Function; | ||
/** @type {array} */ declare const i: any[]; | ||
/** @type {promise} */ declare const j: Promise<any>; | ||
/** @type {Object<string, string>} */ declare const k: { |
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 about add a Object
=> object
?
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.
That's actually not a thing we interpret - Object
with type arguments is interpreted as an index signature, without isn't recognized special (so it looks up the global Object
constructor type, which is the apparent type of... pretty much everything except null
and undefined
) except when noImplicitAny
is false, where it is any
.
getIntendedTypeFromJSDocTypeReference
in the checker has the special jsdoc types that we lookup in it.
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.
Hmm, but the handbook says
Don’t ever use the types Number, String, Boolean, Symbol, or Object These types refer to non-primitive boxed objects that are almost never used appropriately in JavaScript code.
Instead of Object, use the non-primitive object type (added in TypeScript 2.2).
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 think that the point is to test out stuff might people write in JSDoc, because people often write terrible stuff in JSDoc because it was terribly specified.
@@ -40,14 +40,14 @@ lf.Transaction = function() {}; | |||
* @return {!IThenable} | |||
*/ | |||
lf.Transaction.prototype.begin = function(scope) {}; | |||
>lf.Transaction.prototype.begin = function(scope) {} : (scope: Array<lf.schema.Table>) => any | |||
>lf.Transaction.prototype.begin = function(scope) {} : (scope: Array<any>) => any |
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 it a regression?
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.
lf.schema.Table
doesn't actually exist in the test, so we were preserving a node that failed to resolve anyway. Technically we could keep it (since it's an error anyway), but since i changed the default for jsdoc type which fail to resolve in not-jsdoc to serialize the resolved type, it now prints back as the any
the failed resolution returns.
@@ -1,7 +1,7 @@ | |||
=== tests/cases/conformance/jsdoc/foo.js === | |||
/** @param {Image} image */ | |||
function process(image) { | |||
>process : (image: Image) => HTMLImageElement | |||
>process : (image: new (width?: number, height?: number) => HTMLImageElement) => HTMLImageElement |
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.
👀
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.
Yeah, Image
is a constructor with no associated interface
, and jsdoc lookup is strange. So, in TS, (image: Image) => HTMLImageElement
will issue a Image is not a type
error (because it's not). So we gotta serialize the type the jsdoc resolver resolved to instead of keeping the input node.
@typescript-bot cherry-pick this to release-3.9 |
Heya @DanielRosenwasser, I've started to run the task to cherry-pick this into |
Hey @DanielRosenwasser, I couldn't open a PR with the cherry-pick. (You can check the log here). You may need to squash and pick this PR into release-3.9 manually. |
🤖 Pick PR #38368 (Fix js missing type arguments on ex...) into release-3.9
* upstream/master: (54 commits) LEGO: check in for master to temporary branch. LEGO: check in for master to temporary branch. Fix for jsdoc modifiers on constructor params (microsoft#38403) Improve assert message in binder (microsoft#38270) fix broken regex on "src/services/completions.ts#getCompletionData" (microsoft#37546) report error for duplicate @type declaration (microsoft#38340) fix(38073): hide 'Extract to function in global scope' action for arrow functions which use 'this' (microsoft#38107) Update user baselines (microsoft#38472) Update user baselines (microsoft#38405) Changed template strings to emit void 0 instead of undefined (microsoft#38430) Fix js missing type arguments on existing nodes and jsdoc object literal declaration emit (microsoft#38368) LEGO: check in for master to temporary branch. Make isDynamicFileName available publicly (microsoft#38269) LEGO: check in for master to temporary branch. LEGO: check in for master to temporary branch. Exclude arrays and tuples from full intersection property check (microsoft#38395) Fix crash caused by assertion with evolving array type (microsoft#38398) Update user baselines (microsoft#38128) LEGO: check in for master to temporary branch. moveToNewFile: handle namespace imports too ... # Conflicts: # src/compiler/types.ts # src/compiler/utilities.ts
Fixes #38356