mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
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:
+14
-1
@@ -43294,7 +43294,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
|
||||
|
||||
function checkJSDocTypeIsInJsFile(node: Node): void {
|
||||
if (!isInJSFile(node)) {
|
||||
grammarErrorOnNode(node, Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments);
|
||||
if (isJSDocNonNullableType(node) || isJSDocNullableType(node)) {
|
||||
const token = tokenToString(isJSDocNonNullableType(node) ? SyntaxKind.ExclamationToken : SyntaxKind.QuestionToken);
|
||||
const diagnostic = node.postfix
|
||||
? Diagnostics._0_at_the_end_of_a_type_is_not_valid_TypeScript_syntax_Did_you_mean_to_write_1
|
||||
: Diagnostics._0_at_the_start_of_a_type_is_not_valid_TypeScript_syntax_Did_you_mean_to_write_1;
|
||||
const typeNode = node.type;
|
||||
const type = getTypeFromTypeNode(typeNode);
|
||||
grammarErrorOnNode(node, diagnostic, token, typeToString(
|
||||
isJSDocNullableType(node) && !(type === neverType || type === voidType)
|
||||
? getUnionType(append([type, undefinedType], node.postfix ? undefined : nullType)) : type));
|
||||
}
|
||||
else {
|
||||
grammarErrorOnNode(node, Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6545,6 +6545,14 @@
|
||||
"category": "Error",
|
||||
"code": 17018
|
||||
},
|
||||
"'{0}' at the end of a type is not valid TypeScript syntax. Did you mean to write '{1}'?": {
|
||||
"category": "Error",
|
||||
"code": 17019
|
||||
},
|
||||
"'{0}' at the start of a type is not valid TypeScript syntax. Did you mean to write '{1}'?": {
|
||||
"category": "Error",
|
||||
"code": 17020
|
||||
},
|
||||
"Circularity detected while resolving configuration: {0}": {
|
||||
"category": "Error",
|
||||
"code": 18000
|
||||
|
||||
@@ -4809,7 +4809,6 @@ namespace Parser {
|
||||
if (contextFlags & NodeFlags.TypeExcludesFlags) {
|
||||
return doOutsideOfContext(NodeFlags.TypeExcludesFlags, parseType);
|
||||
}
|
||||
|
||||
if (isStartOfFunctionTypeOrConstructorType()) {
|
||||
return parseFunctionOrConstructorType();
|
||||
}
|
||||
|
||||
@@ -809,12 +809,13 @@ export const enum NodeFlags {
|
||||
/** @internal */ PossiblyContainsDynamicImport = 1 << 21,
|
||||
/** @internal */ PossiblyContainsImportMeta = 1 << 22,
|
||||
|
||||
JSDoc = 1 << 23, // If node was parsed inside jsdoc
|
||||
JSDoc = 1 << 23, // If node was parsed inside jsdoc
|
||||
/** @internal */ Ambient = 1 << 24, // If node was inside an ambient context -- a declaration file, or inside something with the `declare` modifier.
|
||||
/** @internal */ InWithStatement = 1 << 25, // If any ancestor of node was the `statement` of a WithStatement (not the `expression`)
|
||||
JsonFile = 1 << 26, // If node was parsed in a Json
|
||||
JsonFile = 1 << 26, // If node was parsed in a Json
|
||||
/** @internal */ TypeCached = 1 << 27, // If a type was cached for node at any point
|
||||
/** @internal */ Deprecated = 1 << 28, // If has '@deprecated' JSDoc tag
|
||||
/** @internal */ ConditionalTypeContext = 1 << 29,
|
||||
|
||||
BlockScoped = Let | Const,
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user