fix(29648): Error message related to JSDoc for non-JSDoc syntax error (#50793)

* fix(29648): improve diagnostics of non-JSDoc syntax errors

* fix lint errors

* update tests

* change diagnostic type suggestion. fix QF for jsdoc nullable type

* move error handling from the parser to the checker

* change diagnostic message. remove speculative parsing

* update baseline
This commit is contained in:
Oleksandr T
2022-12-30 02:50:57 +02:00
committed by GitHub
parent ba393b6b9f
commit 44152bc22e
30 changed files with 667 additions and 76 deletions

View File

@@ -1,4 +1,5 @@
import {
append,
AsExpression,
CallSignatureDeclaration,
CodeFixAction,
@@ -10,6 +11,7 @@ import {
GetAccessorDeclaration,
getTokenAtPosition,
IndexSignatureDeclaration,
isJSDocNullableType,
MappedTypeNode,
MethodDeclaration,
MethodSignature,
@@ -37,7 +39,12 @@ import {
const fixIdPlain = "fixJSDocTypes_plain";
const fixIdNullable = "fixJSDocTypes_nullable";
const errorCodes = [Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments.code];
const errorCodes = [
Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments.code,
Diagnostics._0_at_the_end_of_a_type_is_not_valid_TypeScript_syntax_Did_you_mean_to_write_1.code,
Diagnostics._0_at_the_start_of_a_type_is_not_valid_TypeScript_syntax_Did_you_mean_to_write_1.code,
];
registerCodeFix({
errorCodes,
getCodeActions(context) {
@@ -51,7 +58,7 @@ registerCodeFix({
if (typeNode.kind === SyntaxKind.JSDocNullableType) {
// for nullable types, suggest the flow-compatible `T | null | undefined`
// in addition to the jsdoc/closure-compatible `T | null`
actions.push(fix(checker.getNullableType(type, TypeFlags.Undefined), fixIdNullable, Diagnostics.Change_all_jsdoc_style_types_to_TypeScript_and_add_undefined_to_nullable_types));
actions.push(fix(type, fixIdNullable, Diagnostics.Change_all_jsdoc_style_types_to_TypeScript_and_add_undefined_to_nullable_types));
}
return actions;
@@ -81,7 +88,7 @@ function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, ol
function getInfo(sourceFile: SourceFile, pos: number, checker: TypeChecker): { readonly typeNode: TypeNode, readonly type: Type } | undefined {
const decl = findAncestor(getTokenAtPosition(sourceFile, pos), isTypeContainer);
const typeNode = decl && decl.type;
return typeNode && { typeNode, type: checker.getTypeFromTypeNode(typeNode) };
return typeNode && { typeNode, type: getType(checker, typeNode) };
}
// TODO: GH#19856 Node & { type: TypeNode }
@@ -115,3 +122,15 @@ function isTypeContainer(node: Node): node is TypeContainer {
return false;
}
}
function getType(checker: TypeChecker, node: TypeNode) {
if (isJSDocNullableType(node)) {
const type = checker.getTypeFromTypeNode(node.type);
if (type === checker.getNeverType() || type === checker.getVoidType()) {
return type;
}
return checker.getUnionType(
append([type, checker.getUndefinedType()], node.postfix ? undefined : checker.getNullType()));
}
return checker.getTypeFromTypeNode(node);
}