Skip to content

Commit 7d7a06d

Browse files
authored
Merge pull request #17250 from Microsoft/quickfix-jsdoc-in-ts
Quickfix jsdoc in Typescript files
2 parents 1b7faf1 + 65e8da1 commit 7d7a06d

14 files changed

+88
-2
lines changed

src/compiler/checker.ts

+6
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@ namespace ts {
117117
},
118118
getParameterType: getTypeAtPosition,
119119
getReturnTypeOfSignature,
120+
getNullableType,
120121
getNonNullableType,
121122
typeToTypeNode: nodeBuilder.typeToTypeNode,
122123
indexInfoToIndexSignatureDeclaration: nodeBuilder.indexInfoToIndexSignatureDeclaration,
@@ -9963,6 +9964,11 @@ namespace ts {
99639964
neverType;
99649965
}
99659966

9967+
/**
9968+
* Add undefined or null or both to a type if they are missing.
9969+
* @param type - type to add undefined and/or null to if not present
9970+
* @param flags - Either TypeFlags.Undefined or TypeFlags.Null, or both
9971+
*/
99669972
function getNullableType(type: Type, flags: TypeFlags): Type {
99679973
const missing = (flags & ~type.flags) & (TypeFlags.Undefined | TypeFlags.Null);
99689974
return missing === 0 ? type :

src/compiler/types.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2520,6 +2520,7 @@ namespace ts {
25202520
* Returns `any` if the index is not valid.
25212521
*/
25222522
/* @internal */ getParameterType(signature: Signature, parameterIndex: number): Type;
2523+
getNullableType(type: Type, flags: TypeFlags): Type;
25232524
getNonNullableType(type: Type): Type;
25242525

25252526
/** Note that the resulting nodes cannot be checked. */

src/services/codefixes/disableJsDiagnostics.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ namespace ts.codefix {
3232
}
3333
}
3434

35-
// If all fails, add an extra new line immediatlly before the error span.
35+
// If all fails, add an extra new line immediately before the error span.
3636
return {
3737
span: { start: position, length: 0 },
3838
newText: `${position === startPosition ? "" : newLineCharacter}// @ts-ignore${newLineCharacter}`
@@ -67,4 +67,4 @@ namespace ts.codefix {
6767
}]
6868
}];
6969
}
70-
}
70+
}
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
/* @internal */
2+
namespace ts.codefix {
3+
registerCodeFix({
4+
errorCodes: [Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments.code],
5+
getCodeActions: getActionsForJSDocTypes
6+
});
7+
8+
function getActionsForJSDocTypes(context: CodeFixContext): CodeAction[] | undefined {
9+
const sourceFile = context.sourceFile;
10+
const node = getTokenAtPosition(sourceFile, context.span.start, /*includeJsDocComment*/ false);
11+
const decl = ts.findAncestor(node, n => n.kind === SyntaxKind.VariableDeclaration);
12+
if (!decl) return;
13+
const checker = context.program.getTypeChecker();
14+
15+
const jsdocType = (decl as VariableDeclaration).type;
16+
const original = getTextOfNode(jsdocType);
17+
const type = checker.getTypeFromTypeNode(jsdocType);
18+
const actions = [createAction(jsdocType, sourceFile.fileName, original, checker.typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.NoTruncation))];
19+
if (jsdocType.kind === SyntaxKind.JSDocNullableType) {
20+
// for nullable types, suggest the flow-compatible `T | null | undefined`
21+
// in addition to the jsdoc/closure-compatible `T | null`
22+
const replacementWithUndefined = checker.typeToString(checker.getNullableType(type, TypeFlags.Undefined), /*enclosingDeclaration*/ undefined, TypeFormatFlags.NoTruncation);
23+
actions.push(createAction(jsdocType, sourceFile.fileName, original, replacementWithUndefined));
24+
}
25+
return actions;
26+
}
27+
28+
function createAction(declaration: TypeNode, fileName: string, original: string, replacement: string): CodeAction {
29+
return {
30+
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Change_0_to_1), [original, replacement]),
31+
changes: [{
32+
fileName,
33+
textChanges: [{
34+
span: { start: declaration.getStart(), length: declaration.getWidth() },
35+
newText: replacement
36+
}]
37+
}],
38+
};
39+
}
40+
}

src/services/codefixes/fixes.ts

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
/// <reference path="fixExtendsInterfaceBecomesImplements.ts" />
88
/// <reference path="fixForgottenThisPropertyAccess.ts" />
99
/// <reference path='fixUnusedIdentifier.ts' />
10+
/// <reference path='fixJSDocTypes.ts' />
1011
/// <reference path='importFixes.ts' />
1112
/// <reference path='disableJsDiagnostics.ts' />
1213
/// <reference path='helpers.ts' />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/// <reference path='fourslash.ts' />
2+
//// var x: [|?|] = 12;
3+
4+
verify.rangeAfterCodeFix("any");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/// <reference path='fourslash.ts' />
2+
//// var x: [|*|] = 12;
3+
4+
verify.rangeAfterCodeFix("any");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/// <reference path='fourslash.ts' />
2+
//// var x: [|......number[][]|] = 12;
3+
4+
verify.rangeAfterCodeFix("number[][][][]");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/// <reference path='fourslash.ts' />
2+
//// var x: [|Array.<number>|] = 12;
3+
4+
verify.rangeAfterCodeFix("number[]");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// @strict: true
2+
/// <reference path='fourslash.ts' />
3+
//// var x: [|?number|] = 12;
4+
5+
verify.rangeAfterCodeFix("number | null", /*includeWhiteSpace*/ false, /*errorCode*/ 8020, 0);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// @strict: true
2+
/// <reference path='fourslash.ts' />
3+
//// var x: [|number?|] = 12;
4+
5+
verify.rangeAfterCodeFix("number | null | undefined", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 1);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/// <reference path='fourslash.ts' />
2+
//// var x: [|!number|] = 12;
3+
4+
verify.rangeAfterCodeFix("number");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/// <reference path='fourslash.ts' />
2+
//// var x: [|function(this: number, number): string|] = 12;
3+
4+
verify.rangeAfterCodeFix("(this: number, arg1: number) => string");
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/// <reference path='fourslash.ts' />
2+
//// var x: [|function(new: number)|] = 12;
3+
4+
verify.rangeAfterCodeFix("new () => number");

0 commit comments

Comments
 (0)