Skip to content
6 changes: 6 additions & 0 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ namespace ts {
},
getParameterType: getTypeAtPosition,
getReturnTypeOfSignature,
getNullableType,
getNonNullableType,
typeToTypeNode: nodeBuilder.typeToTypeNode,
indexInfoToIndexSignatureDeclaration: nodeBuilder.indexInfoToIndexSignatureDeclaration,
Expand Down Expand Up @@ -9954,6 +9955,11 @@ namespace ts {
neverType;
}

/**
* Add undefined or null or both to a type if they are missing.
* @param type - type to add undefined and/or null to if not present
* @param flags - Either TypeFlags.Undefined or TypeFlags.Null, or both
*/
function getNullableType(type: Type, flags: TypeFlags): Type {
const missing = (flags & ~type.flags) & (TypeFlags.Undefined | TypeFlags.Null);
return missing === 0 ? type :
Expand Down
1 change: 1 addition & 0 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2520,6 +2520,7 @@ namespace ts {
* Returns `any` if the index is not valid.
*/
/* @internal */ getParameterType(signature: Signature, parameterIndex: number): Type;
getNullableType(type: Type, flags: TypeFlags): Type;
getNonNullableType(type: Type): Type;

/** Note that the resulting nodes cannot be checked. */
Expand Down
4 changes: 2 additions & 2 deletions src/services/codefixes/disableJsDiagnostics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ namespace ts.codefix {
}
}

// If all fails, add an extra new line immediatlly before the error span.
// If all fails, add an extra new line immediately before the error span.
return {
span: { start: position, length: 0 },
newText: `${position === startPosition ? "" : newLineCharacter}// @ts-ignore${newLineCharacter}`
Expand Down Expand Up @@ -67,4 +67,4 @@ namespace ts.codefix {
}]
}];
}
}
}
40 changes: 40 additions & 0 deletions src/services/codefixes/fixJSDocTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/* @internal */
namespace ts.codefix {
registerCodeFix({
errorCodes: [Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments.code],
getCodeActions: getActionsForJSDocTypes
});

function getActionsForJSDocTypes(context: CodeFixContext): CodeAction[] | undefined {
const sourceFile = context.sourceFile;
const node = getTokenAtPosition(sourceFile, context.span.start, /*includeJsDocComment*/ false);
const decl = ts.findAncestor(node, n => n.kind === SyntaxKind.VariableDeclaration);
if (!decl) return;
const checker = context.program.getTypeChecker();

const jsdocType = (decl as VariableDeclaration).type;
const original = getTextOfNode(jsdocType);
const type = checker.getTypeFromTypeNode(jsdocType);
const actions = [createAction(jsdocType, sourceFile.fileName, original, checker.typeToString(type, /*enclosingDeclaration*/ undefined, TypeFormatFlags.NoTruncation))];
if (jsdocType.kind === SyntaxKind.JSDocNullableType) {
// for nullable types, suggest the flow-compatible `T | null | undefined`
// in addition to the jsdoc/closure-compatible `T | null`
const replacementWithUndefined = checker.typeToString(checker.getNullableType(type, TypeFlags.Undefined), /*enclosingDeclaration*/ undefined, TypeFormatFlags.NoTruncation);
actions.push(createAction(jsdocType, sourceFile.fileName, original, replacementWithUndefined));
}
return actions;
}

function createAction(declaration: TypeNode, fileName: string, original: string, replacement: string): CodeAction {
return {
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Change_0_to_1), [original, replacement]),
changes: [{
fileName,
textChanges: [{
span: { start: declaration.getStart(), length: declaration.getWidth() },
newText: replacement
}]
}],
};
}
}
1 change: 1 addition & 0 deletions src/services/codefixes/fixes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
/// <reference path="fixExtendsInterfaceBecomesImplements.ts" />
/// <reference path="fixForgottenThisPropertyAccess.ts" />
/// <reference path='fixUnusedIdentifier.ts' />
/// <reference path='fixJSDocTypes.ts' />
/// <reference path='importFixes.ts' />
/// <reference path='disableJsDiagnostics.ts' />
/// <reference path='helpers.ts' />
4 changes: 4 additions & 0 deletions tests/cases/fourslash/codeFixChangeJSDocSyntax1.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/// <reference path='fourslash.ts' />
//// var x: [|?|] = 12;

verify.rangeAfterCodeFix("any");
4 changes: 4 additions & 0 deletions tests/cases/fourslash/codeFixChangeJSDocSyntax2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/// <reference path='fourslash.ts' />
//// var x: [|*|] = 12;

verify.rangeAfterCodeFix("any");
4 changes: 4 additions & 0 deletions tests/cases/fourslash/codeFixChangeJSDocSyntax3.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/// <reference path='fourslash.ts' />
//// var x: [|......number[][]|] = 12;

verify.rangeAfterCodeFix("number[][][][]");
4 changes: 4 additions & 0 deletions tests/cases/fourslash/codeFixChangeJSDocSyntax4.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/// <reference path='fourslash.ts' />
//// var x: [|Array.<number>|] = 12;

verify.rangeAfterCodeFix("number[]");
5 changes: 5 additions & 0 deletions tests/cases/fourslash/codeFixChangeJSDocSyntax5.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// @strict: true
/// <reference path='fourslash.ts' />
//// var x: [|?number|] = 12;

verify.rangeAfterCodeFix("number | null", /*includeWhiteSpace*/ false, /*errorCode*/ 8020, 0);
5 changes: 5 additions & 0 deletions tests/cases/fourslash/codeFixChangeJSDocSyntax6.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// @strict: true
/// <reference path='fourslash.ts' />
//// var x: [|number?|] = 12;

verify.rangeAfterCodeFix("number | null | undefined", /*includeWhiteSpace*/ undefined, /*errorCode*/ undefined, 1);
4 changes: 4 additions & 0 deletions tests/cases/fourslash/codeFixChangeJSDocSyntax7.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/// <reference path='fourslash.ts' />
//// var x: [|!number|] = 12;

verify.rangeAfterCodeFix("number");
4 changes: 4 additions & 0 deletions tests/cases/fourslash/codeFixChangeJSDocSyntax8.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/// <reference path='fourslash.ts' />
//// var x: [|function(this: number, number): string|] = 12;

verify.rangeAfterCodeFix("(this: number, arg1: number) => string");
4 changes: 4 additions & 0 deletions tests/cases/fourslash/codeFixChangeJSDocSyntax9.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/// <reference path='fourslash.ts' />
//// var x: [|function(new: number)|] = 12;

verify.rangeAfterCodeFix("new () => number");