diff --git a/src/services/codefixes/convertToAsyncFunction.ts b/src/services/codefixes/convertToAsyncFunction.ts index 69a0ef7e221..910cceadcfb 100644 --- a/src/services/codefixes/convertToAsyncFunction.ts +++ b/src/services/codefixes/convertToAsyncFunction.ts @@ -14,17 +14,10 @@ namespace ts.codefix { getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, err) => convertToAsyncFunction(changes, err.file, err.start, context.program.getTypeChecker(), context)), }); - - /* - custom type to encapsulate information for variable declarations synthesized in the refactor - numberOfUsesOriginal - number of times the variable should be assigned in the refactor - numberOfUsesSynthesized - count of how many times the variable has been assigned so far - At the end of the refactor, numberOfUsesOriginal should === numberOfUsesSynthesized - */ interface SynthIdentifier { identifier: Identifier; types: Type[]; - numberOfAssignmentsOriginal: number; + numberOfAssignmentsOriginal: number; // number of times the variable should be assigned in the refactor } interface SymbolAndIdentifier { @@ -380,7 +373,7 @@ namespace ts.codefix { const hasPrevArgName = prevArgName && prevArgName.identifier.text.length > 0; const originalNodeParent = node.original ? node.original.parent : node.parent; if (hasPrevArgName && !shouldReturn && (!originalNodeParent || isPropertyAccessExpression(originalNodeParent))) { - return createVariableDeclarationOrAssignment(prevArgName!, createAwait(node), transformer).concat(); // hack to make the types match + return createTransformedStatement(prevArgName!, createAwait(node), transformer).concat(); // hack to make the types match } else if (!hasPrevArgName && !shouldReturn && (!originalNodeParent || isPropertyAccessExpression(originalNodeParent))) { return [createStatement(createAwait(node))]; @@ -389,7 +382,7 @@ namespace ts.codefix { return [createReturn(getSynthesizedDeepClone(node))]; } - function createVariableDeclarationOrAssignment(prevArgName: SynthIdentifier | undefined, rightHandSide: Expression, transformer: Transformer): NodeArray { + function createTransformedStatement(prevArgName: SynthIdentifier | undefined, rightHandSide: Expression, transformer: Transformer): NodeArray { if (!prevArgName || prevArgName.identifier.text.length === 0) { // if there's no argName to assign to, there still might be side effects return createNodeArray([createStatement(rightHandSide)]); @@ -429,7 +422,7 @@ namespace ts.codefix { break; } const returnType = callSignatures[0].getReturnType(); - const varDeclOrAssignment = createVariableDeclarationOrAssignment(prevArgName, createAwait(synthCall), transformer); + const varDeclOrAssignment = createTransformedStatement(prevArgName, createAwait(synthCall), transformer); if (prevArgName) { prevArgName.types.push(returnType); } @@ -471,12 +464,12 @@ namespace ts.codefix { const type = transformer.checker.getTypeAtLocation(func); const returnType = getLastCallSignature(type, transformer.checker)!.getReturnType(); const rightHandSide = getSynthesizedDeepClone(funcBody); - const possiblyAwaitedRightHandSide = isPromiseReturningExpression(funcBody, transformer.checker) ? createAwait(rightHandSide) : rightHandSide; - const varDeclOrAssignment = createVariableDeclarationOrAssignment(prevArgName, possiblyAwaitedRightHandSide, transformer); + const possiblyAwaitedRightHandSide = !!transformer.checker.getPromisedTypeOfPromise(returnType) ? createAwait(rightHandSide) : rightHandSide; + const transformedStatement = createTransformedStatement(prevArgName, possiblyAwaitedRightHandSide, transformer); if (prevArgName) { prevArgName.types.push(returnType); } - return varDeclOrAssignment; + return transformedStatement; } else { return createNodeArray([createReturn(getSynthesizedDeepClone(funcBody))]); diff --git a/src/testRunner/unittests/convertToAsyncFunction.ts b/src/testRunner/unittests/convertToAsyncFunction.ts index 3f8032275f5..d7e70e4d1e5 100644 --- a/src/testRunner/unittests/convertToAsyncFunction.ts +++ b/src/testRunner/unittests/convertToAsyncFunction.ts @@ -414,6 +414,11 @@ function [#|f|](): Promise { _testConvertToAsyncFunction("convertToAsyncFunction_IgnoreArgs2", ` function [#|f|](): Promise { return fetch('https://typescriptlang.org').then( () => console.log("done") ); +}` + ); + _testConvertToAsyncFunction("convertToAsyncFunction_IgnoreArgs3", ` +function [#|f|](): Promise { + return fetch('https://typescriptlang.org').then( () => console.log("almost done") ).then( () => console.log("done") ); }` ); _testConvertToAsyncFunction("convertToAsyncFunction_Method", ` @@ -1216,6 +1221,12 @@ function [#|f|]() { } `); + +_testConvertToAsyncFunction("convertToAsyncFunction_nestedPromises", ` +function [#|f|]() { + return fetch('https://typescriptlang.org').then(x => Promise.resolve(3).then(y => Promise.resolve(x.statusText.length + y))); +} +`); }); function _testConvertToAsyncFunction(caption: string, text: string) { diff --git a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_nestedPromises.js b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_nestedPromises.js new file mode 100644 index 00000000000..3ce5c421c3d --- /dev/null +++ b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_nestedPromises.js @@ -0,0 +1,13 @@ +// ==ORIGINAL== + +function /*[#|*/f/*|]*/() { + return fetch('https://typescriptlang.org').then(x => Promise.resolve(3).then(y => Promise.resolve(x.statusText.length + y))); +} + +// ==ASYNC FUNCTION::Convert to async function== + +async function f() { + const x = await fetch('https://typescriptlang.org'); + const y = await Promise.resolve(3); + return Promise.resolve(x.statusText.length + y); +} diff --git a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_nestedPromises.ts b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_nestedPromises.ts new file mode 100644 index 00000000000..3ce5c421c3d --- /dev/null +++ b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_nestedPromises.ts @@ -0,0 +1,13 @@ +// ==ORIGINAL== + +function /*[#|*/f/*|]*/() { + return fetch('https://typescriptlang.org').then(x => Promise.resolve(3).then(y => Promise.resolve(x.statusText.length + y))); +} + +// ==ASYNC FUNCTION::Convert to async function== + +async function f() { + const x = await fetch('https://typescriptlang.org'); + const y = await Promise.resolve(3); + return Promise.resolve(x.statusText.length + y); +}