fix(39589): add await before return promise expression (#39649)

This commit is contained in:
Alex T
2020-09-23 01:34:56 +03:00
committed by GitHub
parent d93590e63d
commit c5a28fcdec
13 changed files with 228 additions and 23 deletions

View File

@@ -302,7 +302,6 @@ namespace ts.codefix {
const [onFulfilled, onRejected] = node.arguments;
const onFulfilledArgumentName = getArgBindingName(onFulfilled, transformer);
const transformationBody = getTransformationBody(onFulfilled, prevArgName, onFulfilledArgumentName, node, transformer);
if (onRejected) {
const onRejectedArgumentName = getArgBindingName(onRejected, transformer);
const tryBlock = factory.createBlock(transformExpression(node.expression, transformer, onFulfilledArgumentName).concat(transformationBody));
@@ -310,10 +309,8 @@ namespace ts.codefix {
const catchArg = onRejectedArgumentName ? isSynthIdentifier(onRejectedArgumentName) ? onRejectedArgumentName.identifier.text : onRejectedArgumentName.bindingPattern : "e";
const catchVariableDeclaration = factory.createVariableDeclaration(catchArg);
const catchClause = factory.createCatchClause(catchVariableDeclaration, factory.createBlock(transformationBody2));
return [factory.createTryStatement(tryBlock, catchClause, /* finallyBlock */ undefined)];
}
return transformExpression(node.expression, transformer, onFulfilledArgumentName).concat(transformationBody);
}
@@ -395,11 +392,12 @@ namespace ts.codefix {
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction: {
const funcBody = (func as FunctionExpression | ArrowFunction).body;
const returnType = getLastCallSignature(transformer.checker.getTypeAtLocation(func), transformer.checker)?.getReturnType();
// Arrow functions with block bodies { } will enter this control flow
if (isBlock(funcBody)) {
let refactoredStmts: Statement[] = [];
let seenReturnStatement = false;
for (const statement of funcBody.statements) {
if (isReturnStatement(statement)) {
seenReturnStatement = true;
@@ -407,7 +405,8 @@ namespace ts.codefix {
refactoredStmts = refactoredStmts.concat(getInnerTransformationBody(transformer, [statement], prevArgName));
}
else {
refactoredStmts.push(...maybeAnnotateAndReturn(statement.expression, parent.typeArguments?.[0]));
const possiblyAwaitedRightHandSide = returnType && statement.expression ? getPossiblyAwaitedRightHandSide(transformer.checker, returnType, statement.expression) : statement.expression;
refactoredStmts.push(...maybeAnnotateAndReturn(possiblyAwaitedRightHandSide, parent.typeArguments?.[0]));
}
}
else {
@@ -431,19 +430,21 @@ namespace ts.codefix {
return innerCbBody;
}
const type = transformer.checker.getTypeAtLocation(func);
const returnType = getLastCallSignature(type, transformer.checker)!.getReturnType();
const rightHandSide = getSynthesizedDeepClone(funcBody);
const possiblyAwaitedRightHandSide = !!transformer.checker.getPromisedTypeOfPromise(returnType) ? factory.createAwaitExpression(rightHandSide) : rightHandSide;
if (!shouldReturn(parent, transformer)) {
const transformedStatement = createVariableOrAssignmentOrExpressionStatement(prevArgName, possiblyAwaitedRightHandSide, /*typeAnnotation*/ undefined);
if (prevArgName) {
prevArgName.types.push(returnType);
if (returnType) {
const possiblyAwaitedRightHandSide = getPossiblyAwaitedRightHandSide(transformer.checker, returnType, funcBody);
if (!shouldReturn(parent, transformer)) {
const transformedStatement = createVariableOrAssignmentOrExpressionStatement(prevArgName, possiblyAwaitedRightHandSide, /*typeAnnotation*/ undefined);
if (prevArgName) {
prevArgName.types.push(returnType);
}
return transformedStatement;
}
else {
return maybeAnnotateAndReturn(possiblyAwaitedRightHandSide, parent.typeArguments?.[0]);
}
return transformedStatement;
}
else {
return maybeAnnotateAndReturn(possiblyAwaitedRightHandSide, parent.typeArguments?.[0]);
return silentFail();
}
}
}
@@ -454,12 +455,16 @@ namespace ts.codefix {
return emptyArray;
}
function getPossiblyAwaitedRightHandSide(checker: TypeChecker, type: Type, expr: Expression): AwaitExpression | Expression {
const rightHandSide = getSynthesizedDeepClone(expr);
return !!checker.getPromisedTypeOfPromise(type) ? factory.createAwaitExpression(rightHandSide) : rightHandSide;
}
function getLastCallSignature(type: Type, checker: TypeChecker): Signature | undefined {
const callSignatures = checker.getSignaturesOfType(type, SignatureKind.Call);
return lastOrUndefined(callSignatures);
}
function removeReturns(stmts: readonly Statement[], prevArgName: SynthBindingName | undefined, transformer: Transformer, seenReturnStatement: boolean): readonly Statement[] {
const ret: Statement[] = [];
for (const stmt of stmts) {

View File

@@ -902,7 +902,7 @@ function [#|f|](): Promise<string> {
}
`
);
_testConvertToAsyncFunction("convertToAsyncFunction_PromiseAllAndThen", `
_testConvertToAsyncFunction("convertToAsyncFunction_PromiseAllAndThen1", `
function [#|f|]() {
return Promise.resolve().then(function () {
return Promise.all([fetch("https://typescriptlang.org"), fetch("https://microsoft.com"), Promise.resolve().then(function () {
@@ -921,6 +921,26 @@ function [#|f|]() {
})]).then(res => res.toString());
});
}
`
);
_testConvertToAsyncFunction("convertToAsyncFunction_PromiseAllAndThen3", `
function [#|f|]() {
return Promise.resolve().then(() =>
Promise.all([fetch("https://typescriptlang.org"), fetch("https://microsoft.com"), Promise.resolve().then(function () {
return fetch("https://github.com");
}).then(res => res.toString())]));
}
`
);
_testConvertToAsyncFunction("convertToAsyncFunction_PromiseAllAndThen4", `
function [#|f|]() {
return Promise.resolve().then(() =>
Promise.all([fetch("https://typescriptlang.org"), fetch("https://microsoft.com"), Promise.resolve().then(function () {
return fetch("https://github.com");
})]).then(res => res.toString()));
}
`
);
_testConvertToAsyncFunction("convertToAsyncFunction_Scope1", `
@@ -1124,6 +1144,27 @@ function [#|bar|]<T>(x: T): Promise<T> {
`
);
_testConvertToAsyncFunction("convertToAsyncFunction_Return1", `
function [#|f|](p: Promise<unknown>) {
return p.catch((error: Error) => {
return Promise.reject(error);
});
}`
);
_testConvertToAsyncFunction("convertToAsyncFunction_Return2", `
function [#|f|](p: Promise<unknown>) {
return p.catch((error: Error) => Promise.reject(error));
}`
);
_testConvertToAsyncFunction("convertToAsyncFunction_Return3", `
function [#|f|](p: Promise<unknown>) {
return p.catch(function (error: Error) {
return Promise.reject(error);
});
}`
);
_testConvertToAsyncFunction("convertToAsyncFunction_LocalReturn", `
function [#|f|]() {
@@ -1352,7 +1393,7 @@ function [#|f|]() {
}
`);
_testConvertToAsyncFunction("convertToAsyncFunction_noArgs", `
_testConvertToAsyncFunction("convertToAsyncFunction_noArgs1", `
function delay(millis: number): Promise<void> {
throw "no"
}
@@ -1364,7 +1405,21 @@ function [#|main2|]() {
.then(() => { console.log("."); return delay(500); })
.then(() => { console.log("."); return delay(500); })
}
`);
`);
_testConvertToAsyncFunction("convertToAsyncFunction_noArgs2", `
function delay(millis: number): Promise<void> {
throw "no"
}
function [#|main2|]() {
console.log("Please wait. Loading.");
return delay(500)
.then(() => delay(500))
.then(() => delay(500))
.then(() => delay(500))
}
`);
_testConvertToAsyncFunction("convertToAsyncFunction_exportModifier", `
export function [#|foo|]() {