Skip to content

Commit 44152bc

Browse files
authored
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
1 parent ba393b6 commit 44152bc

30 files changed

+667
-76
lines changed

src/compiler/checker.ts

+14-1
Original file line numberDiff line numberDiff line change
@@ -43294,7 +43294,20 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4329443294

4329543295
function checkJSDocTypeIsInJsFile(node: Node): void {
4329643296
if (!isInJSFile(node)) {
43297-
grammarErrorOnNode(node, Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments);
43297+
if (isJSDocNonNullableType(node) || isJSDocNullableType(node)) {
43298+
const token = tokenToString(isJSDocNonNullableType(node) ? SyntaxKind.ExclamationToken : SyntaxKind.QuestionToken);
43299+
const diagnostic = node.postfix
43300+
? Diagnostics._0_at_the_end_of_a_type_is_not_valid_TypeScript_syntax_Did_you_mean_to_write_1
43301+
: Diagnostics._0_at_the_start_of_a_type_is_not_valid_TypeScript_syntax_Did_you_mean_to_write_1;
43302+
const typeNode = node.type;
43303+
const type = getTypeFromTypeNode(typeNode);
43304+
grammarErrorOnNode(node, diagnostic, token, typeToString(
43305+
isJSDocNullableType(node) && !(type === neverType || type === voidType)
43306+
? getUnionType(append([type, undefinedType], node.postfix ? undefined : nullType)) : type));
43307+
}
43308+
else {
43309+
grammarErrorOnNode(node, Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments);
43310+
}
4329843311
}
4329943312
}
4330043313

src/compiler/diagnosticMessages.json

+8
Original file line numberDiff line numberDiff line change
@@ -6545,6 +6545,14 @@
65456545
"category": "Error",
65466546
"code": 17018
65476547
},
6548+
"'{0}' at the end of a type is not valid TypeScript syntax. Did you mean to write '{1}'?": {
6549+
"category": "Error",
6550+
"code": 17019
6551+
},
6552+
"'{0}' at the start of a type is not valid TypeScript syntax. Did you mean to write '{1}'?": {
6553+
"category": "Error",
6554+
"code": 17020
6555+
},
65486556
"Circularity detected while resolving configuration: {0}": {
65496557
"category": "Error",
65506558
"code": 18000

src/compiler/parser.ts

-1
Original file line numberDiff line numberDiff line change
@@ -4809,7 +4809,6 @@ namespace Parser {
48094809
if (contextFlags & NodeFlags.TypeExcludesFlags) {
48104810
return doOutsideOfContext(NodeFlags.TypeExcludesFlags, parseType);
48114811
}
4812-
48134812
if (isStartOfFunctionTypeOrConstructorType()) {
48144813
return parseFunctionOrConstructorType();
48154814
}

src/compiler/types.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -809,12 +809,13 @@ export const enum NodeFlags {
809809
/** @internal */ PossiblyContainsDynamicImport = 1 << 21,
810810
/** @internal */ PossiblyContainsImportMeta = 1 << 22,
811811

812-
JSDoc = 1 << 23, // If node was parsed inside jsdoc
812+
JSDoc = 1 << 23, // If node was parsed inside jsdoc
813813
/** @internal */ Ambient = 1 << 24, // If node was inside an ambient context -- a declaration file, or inside something with the `declare` modifier.
814814
/** @internal */ InWithStatement = 1 << 25, // If any ancestor of node was the `statement` of a WithStatement (not the `expression`)
815-
JsonFile = 1 << 26, // If node was parsed in a Json
815+
JsonFile = 1 << 26, // If node was parsed in a Json
816816
/** @internal */ TypeCached = 1 << 27, // If a type was cached for node at any point
817817
/** @internal */ Deprecated = 1 << 28, // If has '@deprecated' JSDoc tag
818+
/** @internal */ ConditionalTypeContext = 1 << 29,
818819

819820
BlockScoped = Let | Const,
820821

src/services/codefixes/fixJSDocTypes.ts

+22-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
append,
23
AsExpression,
34
CallSignatureDeclaration,
45
CodeFixAction,
@@ -10,6 +11,7 @@ import {
1011
GetAccessorDeclaration,
1112
getTokenAtPosition,
1213
IndexSignatureDeclaration,
14+
isJSDocNullableType,
1315
MappedTypeNode,
1416
MethodDeclaration,
1517
MethodSignature,
@@ -37,7 +39,12 @@ import {
3739

3840
const fixIdPlain = "fixJSDocTypes_plain";
3941
const fixIdNullable = "fixJSDocTypes_nullable";
40-
const errorCodes = [Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments.code];
42+
const errorCodes = [
43+
Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments.code,
44+
Diagnostics._0_at_the_end_of_a_type_is_not_valid_TypeScript_syntax_Did_you_mean_to_write_1.code,
45+
Diagnostics._0_at_the_start_of_a_type_is_not_valid_TypeScript_syntax_Did_you_mean_to_write_1.code,
46+
];
47+
4148
registerCodeFix({
4249
errorCodes,
4350
getCodeActions(context) {
@@ -51,7 +58,7 @@ registerCodeFix({
5158
if (typeNode.kind === SyntaxKind.JSDocNullableType) {
5259
// for nullable types, suggest the flow-compatible `T | null | undefined`
5360
// in addition to the jsdoc/closure-compatible `T | null`
54-
actions.push(fix(checker.getNullableType(type, TypeFlags.Undefined), fixIdNullable, Diagnostics.Change_all_jsdoc_style_types_to_TypeScript_and_add_undefined_to_nullable_types));
61+
actions.push(fix(type, fixIdNullable, Diagnostics.Change_all_jsdoc_style_types_to_TypeScript_and_add_undefined_to_nullable_types));
5562
}
5663
return actions;
5764

@@ -81,7 +88,7 @@ function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, ol
8188
function getInfo(sourceFile: SourceFile, pos: number, checker: TypeChecker): { readonly typeNode: TypeNode, readonly type: Type } | undefined {
8289
const decl = findAncestor(getTokenAtPosition(sourceFile, pos), isTypeContainer);
8390
const typeNode = decl && decl.type;
84-
return typeNode && { typeNode, type: checker.getTypeFromTypeNode(typeNode) };
91+
return typeNode && { typeNode, type: getType(checker, typeNode) };
8592
}
8693

8794
// TODO: GH#19856 Node & { type: TypeNode }
@@ -115,3 +122,15 @@ function isTypeContainer(node: Node): node is TypeContainer {
115122
return false;
116123
}
117124
}
125+
126+
function getType(checker: TypeChecker, node: TypeNode) {
127+
if (isJSDocNullableType(node)) {
128+
const type = checker.getTypeFromTypeNode(node.type);
129+
if (type === checker.getNeverType() || type === checker.getVoidType()) {
130+
return type;
131+
}
132+
return checker.getUnionType(
133+
append([type, checker.getUndefinedType()], node.postfix ? undefined : checker.getNullType()));
134+
}
135+
return checker.getTypeFromTypeNode(node);
136+
}

tests/baselines/reference/decoratorMetadata-jsdoc.errors.txt

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
tests/cases/conformance/decorators/decoratorMetadata-jsdoc.ts(5,9): error TS8020: JSDoc types can only be used inside documentation comments.
2-
tests/cases/conformance/decorators/decoratorMetadata-jsdoc.ts(7,9): error TS8020: JSDoc types can only be used inside documentation comments.
1+
tests/cases/conformance/decorators/decoratorMetadata-jsdoc.ts(5,9): error TS17019: '?' at the end of a type is not valid TypeScript syntax. Did you mean to write 'string'?
2+
tests/cases/conformance/decorators/decoratorMetadata-jsdoc.ts(7,9): error TS17019: '!' at the end of a type is not valid TypeScript syntax. Did you mean to write 'string'?
33
tests/cases/conformance/decorators/decoratorMetadata-jsdoc.ts(9,9): error TS8020: JSDoc types can only be used inside documentation comments.
44

55

@@ -10,11 +10,11 @@ tests/cases/conformance/decorators/decoratorMetadata-jsdoc.ts(9,9): error TS8020
1010
@decorator()
1111
a?: string?;
1212
~~~~~~~
13-
!!! error TS8020: JSDoc types can only be used inside documentation comments.
13+
!!! error TS17019: '?' at the end of a type is not valid TypeScript syntax. Did you mean to write 'string'?
1414
@decorator()
1515
b?: string!;
1616
~~~~~~~
17-
!!! error TS8020: JSDoc types can only be used inside documentation comments.
17+
!!! error TS17019: '!' at the end of a type is not valid TypeScript syntax. Did you mean to write 'string'?
1818
@decorator()
1919
c?: *;
2020
~
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,23 @@
11
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(9,21): error TS8020: JSDoc types can only be used inside documentation comments.
2-
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(10,20): error TS8020: JSDoc types can only be used inside documentation comments.
3-
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(11,21): error TS8020: JSDoc types can only be used inside documentation comments.
4-
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(12,23): error TS8020: JSDoc types can only be used inside documentation comments.
5-
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(12,24): error TS8020: JSDoc types can only be used inside documentation comments.
2+
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(10,20): error TS17019: '?' at the end of a type is not valid TypeScript syntax. Did you mean to write 'string | undefined'?
3+
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(11,21): error TS17020: '?' at the start of a type is not valid TypeScript syntax. Did you mean to write 'string | null | undefined'?
4+
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(12,23): error TS17020: '?' at the start of a type is not valid TypeScript syntax. Did you mean to write 'string | null | undefined'?
5+
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(12,24): error TS17019: '?' at the end of a type is not valid TypeScript syntax. Did you mean to write 'string | undefined'?
66
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(14,28): error TS8020: JSDoc types can only be used inside documentation comments.
7-
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(15,27): error TS8020: JSDoc types can only be used inside documentation comments.
8-
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(16,28): error TS8020: JSDoc types can only be used inside documentation comments.
9-
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(17,30): error TS8020: JSDoc types can only be used inside documentation comments.
10-
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(17,31): error TS8020: JSDoc types can only be used inside documentation comments.
7+
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(15,27): error TS17019: '?' at the end of a type is not valid TypeScript syntax. Did you mean to write 'string | undefined'?
8+
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(16,28): error TS17020: '?' at the start of a type is not valid TypeScript syntax. Did you mean to write 'string | null | undefined'?
9+
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(17,30): error TS17020: '?' at the start of a type is not valid TypeScript syntax. Did you mean to write 'string | null | undefined'?
10+
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(17,31): error TS17019: '?' at the end of a type is not valid TypeScript syntax. Did you mean to write 'string | undefined'?
1111
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(19,21): error TS8020: JSDoc types can only be used inside documentation comments.
12-
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(20,20): error TS8020: JSDoc types can only be used inside documentation comments.
13-
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(21,21): error TS8020: JSDoc types can only be used inside documentation comments.
14-
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(22,23): error TS8020: JSDoc types can only be used inside documentation comments.
15-
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(22,24): error TS8020: JSDoc types can only be used inside documentation comments.
12+
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(20,20): error TS17019: '?' at the end of a type is not valid TypeScript syntax. Did you mean to write 'string | undefined'?
13+
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(21,21): error TS17020: '?' at the start of a type is not valid TypeScript syntax. Did you mean to write 'string | null | undefined'?
14+
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(22,23): error TS17020: '?' at the start of a type is not valid TypeScript syntax. Did you mean to write 'string | null | undefined'?
15+
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(22,24): error TS17019: '?' at the end of a type is not valid TypeScript syntax. Did you mean to write 'string | undefined'?
1616
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(24,28): error TS8020: JSDoc types can only be used inside documentation comments.
17-
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(25,27): error TS8020: JSDoc types can only be used inside documentation comments.
18-
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(26,28): error TS8020: JSDoc types can only be used inside documentation comments.
19-
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(27,30): error TS8020: JSDoc types can only be used inside documentation comments.
20-
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(27,31): error TS8020: JSDoc types can only be used inside documentation comments.
17+
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(25,27): error TS17019: '?' at the end of a type is not valid TypeScript syntax. Did you mean to write 'string | undefined'?
18+
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(26,28): error TS17020: '?' at the start of a type is not valid TypeScript syntax. Did you mean to write 'string | null | undefined'?
19+
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(27,30): error TS17020: '?' at the start of a type is not valid TypeScript syntax. Did you mean to write 'string | null | undefined'?
20+
tests/cases/compiler/expressionWithJSDocTypeArguments.ts(27,31): error TS17019: '?' at the end of a type is not valid TypeScript syntax. Did you mean to write 'string | undefined'?
2121

2222

2323
==== tests/cases/compiler/expressionWithJSDocTypeArguments.ts (20 errors) ====
@@ -34,58 +34,58 @@ tests/cases/compiler/expressionWithJSDocTypeArguments.ts(27,31): error TS8020: J
3434
!!! error TS8020: JSDoc types can only be used inside documentation comments.
3535
const HuhFoo = foo<string?>;
3636
~~~~~~~
37-
!!! error TS8020: JSDoc types can only be used inside documentation comments.
37+
!!! error TS17019: '?' at the end of a type is not valid TypeScript syntax. Did you mean to write 'string | undefined'?
3838
const NopeFoo = foo<?string>;
3939
~~~~~~~
40-
!!! error TS8020: JSDoc types can only be used inside documentation comments.
40+
!!! error TS17020: '?' at the start of a type is not valid TypeScript syntax. Did you mean to write 'string | null | undefined'?
4141
const ComeOnFoo = foo<?string?>;
4242
~~~~~~~~
43-
!!! error TS8020: JSDoc types can only be used inside documentation comments.
43+
!!! error TS17020: '?' at the start of a type is not valid TypeScript syntax. Did you mean to write 'string | null | undefined'?
4444
~~~~~~~
45-
!!! error TS8020: JSDoc types can only be used inside documentation comments.
45+
!!! error TS17019: '?' at the end of a type is not valid TypeScript syntax. Did you mean to write 'string | undefined'?
4646

4747
type TWhatFoo = typeof foo<?>;
4848
~
4949
!!! error TS8020: JSDoc types can only be used inside documentation comments.
5050
type THuhFoo = typeof foo<string?>;
5151
~~~~~~~
52-
!!! error TS8020: JSDoc types can only be used inside documentation comments.
52+
!!! error TS17019: '?' at the end of a type is not valid TypeScript syntax. Did you mean to write 'string | undefined'?
5353
type TNopeFoo = typeof foo<?string>;
5454
~~~~~~~
55-
!!! error TS8020: JSDoc types can only be used inside documentation comments.
55+
!!! error TS17020: '?' at the start of a type is not valid TypeScript syntax. Did you mean to write 'string | null | undefined'?
5656
type TComeOnFoo = typeof foo<?string?>;
5757
~~~~~~~~
58-
!!! error TS8020: JSDoc types can only be used inside documentation comments.
58+
!!! error TS17020: '?' at the start of a type is not valid TypeScript syntax. Did you mean to write 'string | null | undefined'?
5959
~~~~~~~
60-
!!! error TS8020: JSDoc types can only be used inside documentation comments.
60+
!!! error TS17019: '?' at the end of a type is not valid TypeScript syntax. Did you mean to write 'string | undefined'?
6161

6262
const WhatBar = Bar<?>;
6363
~
6464
!!! error TS8020: JSDoc types can only be used inside documentation comments.
6565
const HuhBar = Bar<string?>;
6666
~~~~~~~
67-
!!! error TS8020: JSDoc types can only be used inside documentation comments.
67+
!!! error TS17019: '?' at the end of a type is not valid TypeScript syntax. Did you mean to write 'string | undefined'?
6868
const NopeBar = Bar<?string>;
6969
~~~~~~~
70-
!!! error TS8020: JSDoc types can only be used inside documentation comments.
70+
!!! error TS17020: '?' at the start of a type is not valid TypeScript syntax. Did you mean to write 'string | null | undefined'?
7171
const ComeOnBar = Bar<?string?>;
7272
~~~~~~~~
73-
!!! error TS8020: JSDoc types can only be used inside documentation comments.
73+
!!! error TS17020: '?' at the start of a type is not valid TypeScript syntax. Did you mean to write 'string | null | undefined'?
7474
~~~~~~~
75-
!!! error TS8020: JSDoc types can only be used inside documentation comments.
75+
!!! error TS17019: '?' at the end of a type is not valid TypeScript syntax. Did you mean to write 'string | undefined'?
7676

7777
type TWhatBar = typeof Bar<?>;
7878
~
7979
!!! error TS8020: JSDoc types can only be used inside documentation comments.
8080
type THuhBar = typeof Bar<string?>;
8181
~~~~~~~
82-
!!! error TS8020: JSDoc types can only be used inside documentation comments.
82+
!!! error TS17019: '?' at the end of a type is not valid TypeScript syntax. Did you mean to write 'string | undefined'?
8383
type TNopeBar = typeof Bar<?string>;
8484
~~~~~~~
85-
!!! error TS8020: JSDoc types can only be used inside documentation comments.
85+
!!! error TS17020: '?' at the start of a type is not valid TypeScript syntax. Did you mean to write 'string | null | undefined'?
8686
type TComeOnBar = typeof Bar<?string?>;
8787
~~~~~~~~
88-
!!! error TS8020: JSDoc types can only be used inside documentation comments.
88+
!!! error TS17020: '?' at the start of a type is not valid TypeScript syntax. Did you mean to write 'string | null | undefined'?
8989
~~~~~~~
90-
!!! error TS8020: JSDoc types can only be used inside documentation comments.
90+
!!! error TS17019: '?' at the end of a type is not valid TypeScript syntax. Did you mean to write 'string | undefined'?
9191

0 commit comments

Comments
 (0)