Add 'Remove unnecessary await' suggestion and fix (#32363)

* Add remove unnecessary await fix

* Add test for removing unnecessary parens after await is gone

* Fix handling of numbers in property access expressions

* Don’t offer suggestion when awaited type is any/unknown

* Fix random other test

* Fix new expression edge cases

* Only remove parens for identifiers and call expressions
This commit is contained in:
Andrew Branch 2019-07-12 11:03:20 -07:00 committed by GitHub
parent 60a1b1dc1a
commit 89badcc9d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 97 additions and 5 deletions

View File

@ -26403,7 +26403,11 @@ namespace ts {
* The runtime behavior of the `await` keyword.
*/
function checkAwaitedType(type: Type, errorNode: Node, diagnosticMessage: DiagnosticMessage, arg0?: string | number): Type {
return getAwaitedType(type, errorNode, diagnosticMessage, arg0) || errorType;
const awaitedType = getAwaitedType(type, errorNode, diagnosticMessage, arg0);
if (awaitedType === type && !(type.flags & TypeFlags.AnyOrUnknown)) {
addErrorOrSuggestion(/*isError*/ false, createDiagnosticForNode(errorNode, Diagnostics.await_has_no_effect_on_the_type_of_this_expression));
}
return awaitedType || errorType;
}
function getAwaitedType(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage, arg0?: string | number): Type | undefined {

View File

@ -4635,6 +4635,11 @@
"category": "Suggestion",
"code": 80006
},
"'await' has no effect on the type of this expression.": {
"category": "Suggestion",
"code": 80007
},
"Add missing 'super()' call": {
"category": "Message",
"code": 90001
@ -5095,6 +5100,14 @@
"category": "Message",
"code": 95085
},
"Remove unnecessary 'await'": {
"category": "Message",
"code": 95086
},
"Remove all unnecessary uses of 'await'": {
"category": "Message",
"code": 95087
},
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer.": {
"category": "Error",

View File

@ -0,0 +1,33 @@
/* @internal */
namespace ts.codefix {
const fixId = "removeUnnecessaryAwait";
const errorCodes = [
Diagnostics.await_has_no_effect_on_the_type_of_this_expression.code,
];
registerCodeFix({
errorCodes,
getCodeActions: (context) => {
const changes = textChanges.ChangeTracker.with(context, t => makeChange(t, context.sourceFile, context.span));
if (changes.length > 0) {
return [createCodeFixAction(fixId, changes, Diagnostics.Remove_unnecessary_await, fixId, Diagnostics.Remove_all_unnecessary_uses_of_await)];
}
},
fixIds: [fixId],
getAllCodeActions: context => {
return codeFixAll(context, errorCodes, (changes, diag) => makeChange(changes, diag.file, diag));
},
});
function makeChange(changeTracker: textChanges.ChangeTracker, sourceFile: SourceFile, span: TextSpan) {
const awaitKeyword = tryCast(getTokenAtPosition(sourceFile, span.start), (node): node is AwaitKeywordToken => node.kind === SyntaxKind.AwaitKeyword);
const awaitExpression = awaitKeyword && tryCast(awaitKeyword.parent, isAwaitExpression);
if (!awaitExpression) {
return;
}
const parenthesizedExpression = tryCast(awaitExpression.parent, isParenthesizedExpression);
const removeParens = parenthesizedExpression && (isIdentifier(awaitExpression.expression) || isCallExpression(awaitExpression.expression));
changeTracker.replaceNode(sourceFile, removeParens ? parenthesizedExpression || awaitExpression : awaitExpression, awaitExpression.expression);
}
}

View File

@ -80,6 +80,7 @@
"codefixes/useDefaultImport.ts",
"codefixes/fixAddModuleReferTypeMissingTypeof.ts",
"codefixes/convertToMappedObjectType.ts",
"codefixes/removeUnnecessaryAwait.ts",
"refactors/convertExport.ts",
"refactors/convertImport.ts",
"refactors/extractSymbol.ts",

View File

@ -0,0 +1,40 @@
/// <reference path="fourslash.ts" />
////declare class C { foo(): void }
////declare function foo(): string;
////async function f() {
//// await "";
//// await 0;
//// (await foo()).toLowerCase();
//// (await 0).toFixed();
//// (await new C).foo();
////}
verify.codeFix({
description: ts.Diagnostics.Remove_unnecessary_await.message,
index: 0,
newFileContent:
`declare class C { foo(): void }
declare function foo(): string;
async function f() {
"";
await 0;
(await foo()).toLowerCase();
(await 0).toFixed();
(await new C).foo();
}`
});
verify.codeFixAll({
fixAllDescription: ts.Diagnostics.Remove_all_unnecessary_uses_of_await.message,
fixId: "removeUnnecessaryAwait",
newFileContent:
`declare class C { foo(): void }
declare function foo(): string;
async function f() {
"";
0;
foo().toLowerCase();
(0).toFixed();
(new C).foo();
}`
});

View File

@ -2,13 +2,14 @@
// @allowNonTsExtensions: true
// @Filename: test123.js
// @lib: es5
////export function /**/MyClass() {
////}
////MyClass.prototype.foo = async function() {
//// await 2;
//// await Promise.resolve();
////}
////MyClass.bar = async function() {
//// await 3;
//// await Promise.resolve();
////}
verify.codeFix({
@ -18,10 +19,10 @@ verify.codeFix({
constructor() {
}
async foo() {
await 2;
await Promise.resolve();
}
static async bar() {
await 3;
await Promise.resolve();
}
}
`,