Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quickfix jsdoc in Typescript files #17250

Merged
merged 10 commits into from
Jul 18, 2017
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");