-
Notifications
You must be signed in to change notification settings - Fork 13.2k
Allow type predicates in JSDoc #26343
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
1. Parse type predicates. Note that they are parsed everywhere, and get the appropriate error when used places besides a return type. 2. When creating a type predicate, correctly find the function's parameters starting from the jsdoc return type.
src/compiler/checker.ts
Outdated
| function createTypePredicateFromTypePredicateNode(node: TypePredicateNode): IdentifierTypePredicate | ThisTypePredicate { | ||
| const { parameterName } = node; | ||
| const type = getTypeFromTypeNode(node.type); | ||
| const func = isJSDocTypeExpression(node.parent) ? getJSDocHost(node.parent) as FunctionLikeDeclaration : node.parent; |
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.
node.parent.kind === SyntaxKind.JSDocTypeExpression; is a compile error. (#25316)
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 the correct fix is to change the type of TypePredicateNode.parent. Do you 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.
👍
Also, the only reason the cast is valid is because the only place we call this function is getEffectiveReturnTypeNode(signature.declaration); -- could just pass signature.declaration in as a parameter func: SignatureDeclaration | JSDocSignature to avoid computing this.
Then getTypePredicateParameterIndex needs to be updated to function getTypePredicateParameterIndex(parameterList: ReadonlyArray<ParameterDeclaration | JSDocParameterTag>, parameter: Identifier) (and remove the if since parameters are non-optional).
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.
Previous comment may have been a little rambling so I'll rewrite. It is basically 3 comments.
In createTypePredicateFromTypePredicate you compute const func = ...etc....
createTypePredicateFromTypePredicate is only called in exactly one place, getTypePredicateOfSignature. In getTypePredicateOfSignature you already have access to signature.declaration. So you could add a second parameter to createTypeFromTypePredicateNode, func: SignatureDeclaration | JSDocSignature, rather than computing it inside the function.
I also had a somewhat unrelated comment to make: getTypePredicateParameterIndex can take defined inputs, so parameterList: NodeArray<ParameterDeclaration>, parameter: Identifier instead of parameterList: NodeArray<ParameterDeclaration> | undefined, parameter: Identifier | undefined. Then you can remove the if at the start of that function.
But, you'll note that once we are passing in func as a parameter without a cast, we've needed to allow JSDocSignature. That means that parameterList in getTypePredicateParameterIndex needs to be of type ReadonlyArray<ParameterDeclaration | JSDocParameterTag>, which just happens to work without changing any of the actual code in that function.
src/compiler/checker.ts
Outdated
| const func = isJSDocTypeExpression(node.parent) ? getJSDocHost(node.parent) as FunctionLikeDeclaration : node.parent; | ||
| if (parameterName.kind === SyntaxKind.Identifier) { | ||
| return createIdentifierTypePredicate( | ||
| parameterName && parameterName.escapedText as string, // TODO: GH#18217 |
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 just accessed a property on parameterName, so it must be defined.
|
Failing test case: /**
* @callback Cb
* @param {unknown} x
* @return {x is number}
*/
/** @type {Cb} */
function isNumber(x) { return typeof x === "number" }
/** @param {unknown} x */
function g(x) {
if (isNumber(x)) {
x * 2;
}
} |
Also move createTypePredicateFromTypePredicateNode closer to its use
Previously, non-this type predicates almost worked. They just needed a couple of lines of code.
Fixes #26297