Refactoring promises returning functions to use async and await (#26373)

This commit is contained in:
Elizabeth Dinella 2018-08-10 16:11:45 -07:00 committed by GitHub
parent 08f5edbd03
commit 421730a68d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
82 changed files with 3589 additions and 6 deletions

View file

@ -120,6 +120,7 @@ namespace ts {
return node ? getTypeFromTypeNode(node) : errorType;
},
getParameterType: getTypeAtPosition,
getPromisedTypeOfPromise,
getReturnTypeOfSignature,
getNullableType,
getNonNullableType,

View file

@ -4183,7 +4183,10 @@
"category": "Suggestion",
"code": 80005
},
"This may be converted to an async function.": {
"category": "Suggestion",
"code": 80006
},
"Add missing 'super()' call": {
"category": "Message",
"code": 90001
@ -4556,6 +4559,7 @@
"category": "Message",
"code": 95062
},
"Add missing enum member '{0}'": {
"category": "Message",
"code": 95063
@ -4563,5 +4567,13 @@
"Add all missing imports": {
"category": "Message",
"code": 95064
},
"Convert to async function":{
"category": "Message",
"code": 95065
},
"Convert all to async functions": {
"category": "Message",
"code": 95066
}
}
}

View file

@ -2911,6 +2911,8 @@ namespace ts {
getBaseTypes(type: InterfaceType): BaseType[];
getBaseTypeOfLiteralType(type: Type): Type;
getWidenedType(type: Type): Type;
/* @internal */
getPromisedTypeOfPromise(promise: Type, errorNode?: Node): Type | undefined;
getReturnTypeOfSignature(signature: Signature): Type;
/**
* Gets the type of a parameter at a given position in a signature.

View file

@ -0,0 +1,538 @@
/* @internal */
namespace ts.codefix {
const fixId = "convertToAsyncFunction";
const errorCodes = [Diagnostics.This_may_be_converted_to_an_async_function.code];
registerCodeFix({
errorCodes,
getCodeActions(context: CodeFixContext) {
const changes = textChanges.ChangeTracker.with(context, (t) => convertToAsyncFunction(t, context.sourceFile, context.span.start, context.program.getTypeChecker(), context));
return [createCodeFixAction(fixId, changes, Diagnostics.Convert_to_async_function, fixId, Diagnostics.Convert_all_to_async_functions)];
},
fixIds: [fixId],
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;
}
interface SymbolAndIdentifier {
identifier: Identifier;
symbol: Symbol;
}
interface Transformer {
checker: TypeChecker;
synthNamesMap: Map<SynthIdentifier>; // keys are the symbol id of the identifier
allVarNames: SymbolAndIdentifier[];
setOfExpressionsToReturn: Map<true>; // keys are the node ids of the expressions
constIdentifiers: Identifier[];
originalTypeMap: Map<Type>; // keys are the node id of the identifier
isInJSFile: boolean;
}
function convertToAsyncFunction(changes: textChanges.ChangeTracker, sourceFile: SourceFile, position: number, checker: TypeChecker, context: CodeFixContextBase): void {
// get the function declaration - returns a promise
const functionToConvert: FunctionLikeDeclaration = getContainingFunction(getTokenAtPosition(sourceFile, position)) as FunctionLikeDeclaration;
if (!functionToConvert) {
return;
}
const synthNamesMap: Map<SynthIdentifier> = createMap();
const originalTypeMap: Map<Type> = createMap();
const allVarNames: SymbolAndIdentifier[] = [];
const isInJSFile = isInJavaScriptFile(functionToConvert);
const setOfExpressionsToReturn = getAllPromiseExpressionsToReturn(functionToConvert, checker);
const functionToConvertRenamed: FunctionLikeDeclaration = renameCollidingVarNames(functionToConvert, checker, synthNamesMap, context, setOfExpressionsToReturn, originalTypeMap, allVarNames);
const constIdentifiers = getConstIdentifiers(synthNamesMap);
const returnStatements = getReturnStatementsWithPromiseHandlers(functionToConvertRenamed);
const transformer = { checker, synthNamesMap, allVarNames, setOfExpressionsToReturn, constIdentifiers, originalTypeMap, isInJSFile };
if (!returnStatements.length) {
return;
}
// add the async keyword
changes.insertModifierBefore(sourceFile, SyntaxKind.AsyncKeyword, functionToConvert);
function startTransformation(node: CallExpression, nodeToReplace: Node) {
const newNodes = transformExpression(node, transformer, node);
changes.replaceNodeWithNodes(sourceFile, nodeToReplace, newNodes);
}
for (const statement of returnStatements) {
if (isCallExpression(statement)) {
startTransformation(statement, statement);
}
else {
forEachChild(statement, function visit(node: Node) {
if (isCallExpression(node)) {
startTransformation(node, statement);
}
else if (!isFunctionLike(node)) {
forEachChild(node, visit);
}
});
}
}
}
// Returns the identifiers that are never reassigned in the refactor
function getConstIdentifiers(synthNamesMap: Map<SynthIdentifier>): Identifier[] {
const constIdentifiers: Identifier[] = [];
synthNamesMap.forEach((val) => {
if (val.numberOfAssignmentsOriginal === 0) {
constIdentifiers.push(val.identifier);
}
});
return constIdentifiers;
}
/*
Finds all of the expressions of promise type that should not be saved in a variable during the refactor
*/
function getAllPromiseExpressionsToReturn(func: FunctionLikeDeclaration, checker: TypeChecker): Map<true> {
if (!func.body) {
return createMap<true>();
}
const setOfExpressionsToReturn: Map<true> = createMap<true>();
forEachChild(func.body, function visit(node: Node) {
if (isPromiseReturningExpression(node, checker, "then")) {
setOfExpressionsToReturn.set(getNodeId(node).toString(), true);
forEach((<CallExpression>node).arguments, visit);
}
else if (isPromiseReturningExpression(node, checker, "catch")) {
setOfExpressionsToReturn.set(getNodeId(node).toString(), true);
// if .catch() is the last call in the chain, move leftward in the chain until we hit something else that should be returned
forEachChild(node, visit);
}
else if (isPromiseReturningExpression(node, checker)) {
setOfExpressionsToReturn.set(getNodeId(node).toString(), true);
// don't recurse here, since we won't refactor any children or arguments of the expression
}
else {
forEachChild(node, visit);
}
});
return setOfExpressionsToReturn;
}
/*
Returns true if node is a promise returning expression
If name is not undefined, node is a promise returning call of name
*/
function isPromiseReturningExpression(node: Node, checker: TypeChecker, name?: string): boolean {
const isNodeExpression = name ? isCallExpression(node) : isExpression(node);
const isExpressionOfName = isNodeExpression && (!name || hasPropertyAccessExpressionWithName(node as CallExpression, name));
const nodeType = isExpressionOfName && checker.getTypeAtLocation(node);
return !!(nodeType && checker.getPromisedTypeOfPromise(nodeType));
}
function declaredInFile(symbol: Symbol, sourceFile: SourceFile): boolean {
return symbol.valueDeclaration && symbol.valueDeclaration.getSourceFile() === sourceFile;
}
/*
Renaming of identifiers may be neccesary as the refactor changes scopes -
This function collects all existing identifier names and names of identifiers that will be created in the refactor.
It then checks for any collisions and renames them through getSynthesizedDeepClone
*/
function renameCollidingVarNames(nodeToRename: FunctionLikeDeclaration, checker: TypeChecker, synthNamesMap: Map<SynthIdentifier>, context: CodeFixContextBase, setOfAllExpressionsToReturn: Map<true>, originalType: Map<Type>, allVarNames: SymbolAndIdentifier[]): FunctionLikeDeclaration {
const identsToRenameMap: Map<Identifier> = createMap(); // key is the symbol id
forEachChild(nodeToRename, function visit(node: Node) {
if (!isIdentifier(node)) {
forEachChild(node, visit);
return;
}
const symbol = checker.getSymbolAtLocation(node);
const isDefinedInFile = symbol && declaredInFile(symbol, context.sourceFile);
if (symbol && isDefinedInFile) {
const type = checker.getTypeAtLocation(node);
const lastCallSignature = getLastCallSignature(type, checker);
const symbolIdString = getSymbolId(symbol).toString();
// if the identifier refers to a function we want to add the new synthesized variable for the declaration (ex. blob in let blob = res(arg))
// Note - the choice of the last call signature is arbitrary
if (lastCallSignature && lastCallSignature.parameters.length && !synthNamesMap.has(symbolIdString)) {
const synthName = getNewNameIfConflict(createIdentifier(lastCallSignature.parameters[0].name), allVarNames);
synthNamesMap.set(symbolIdString, synthName);
allVarNames.push({ identifier: synthName.identifier, symbol });
}
// we only care about identifiers that are parameters and declarations (don't care about other uses)
else if (node.parent && (isParameter(node.parent) || isVariableDeclaration(node.parent))) {
// if the identifier name conflicts with a different identifier that we've already seen
if (allVarNames.some(ident => ident.identifier.text === node.text && ident.symbol !== symbol)) {
const newName = getNewNameIfConflict(node, allVarNames);
identsToRenameMap.set(symbolIdString, newName.identifier);
synthNamesMap.set(symbolIdString, newName);
allVarNames.push({ identifier: newName.identifier, symbol });
}
else {
const identifier = getSynthesizedDeepClone(node);
identsToRenameMap.set(symbolIdString, identifier);
synthNamesMap.set(symbolIdString, { identifier, types: [], numberOfAssignmentsOriginal: allVarNames.filter(elem => elem.identifier.text === node.text).length/*, numberOfAssignmentsSynthesized: 0*/ });
if ((isParameter(node.parent) && isExpressionOrCallOnTypePromise(node.parent.parent)) || isVariableDeclaration(node.parent)) {
allVarNames.push({ identifier, symbol });
}
}
}
}
});
return getSynthesizedDeepCloneWithRenames(nodeToRename, /*includeTrivia*/ true, identsToRenameMap, checker, deepCloneCallback);
function isExpressionOrCallOnTypePromise(child: Node): boolean {
const node = child.parent;
if (isCallExpression(node) || isIdentifier(node) && !setOfAllExpressionsToReturn.get(getNodeId(node).toString())) {
const nodeType = checker.getTypeAtLocation(node);
const isPromise = nodeType && checker.getPromisedTypeOfPromise(nodeType);
return !!isPromise;
}
return false;
}
function deepCloneCallback(node: Node, clone: Node) {
if (isIdentifier(node)) {
const symbol = checker.getSymbolAtLocation(node);
const symboldIdString = symbol && getSymbolId(symbol).toString();
const renameInfo = symbol && synthNamesMap.get(symboldIdString!);
if (renameInfo) {
const type = checker.getTypeAtLocation(node);
if (type) {
originalType.set(getNodeId(clone).toString(), type);
}
}
}
const val = setOfAllExpressionsToReturn.get(getNodeId(node).toString());
if (val !== undefined) {
setOfAllExpressionsToReturn.delete(getNodeId(node).toString());
setOfAllExpressionsToReturn.set(getNodeId(clone).toString(), val);
}
}
}
function getNewNameIfConflict(name: Identifier, allVarNames: SymbolAndIdentifier[]): SynthIdentifier {
const numVarsSameName = allVarNames.filter(elem => elem.identifier.text === name.text).length;
const numberOfAssignmentsOriginal = 0;
const identifier = numVarsSameName === 0 ? name : createIdentifier(name.text + "_" + numVarsSameName);
return { identifier, types: [], numberOfAssignmentsOriginal };
}
// dispatch function to recursively build the refactoring
function transformExpression(node: Expression, transformer: Transformer, outermostParent: CallExpression, prevArgName?: SynthIdentifier): Statement[] {
if (!node) {
return [];
}
const originalType = isIdentifier(node) && transformer.originalTypeMap.get(getNodeId(node).toString());
const nodeType = originalType || transformer.checker.getTypeAtLocation(node);
if (isCallExpression(node) && hasPropertyAccessExpressionWithName(node, "then") && nodeType && !!transformer.checker.getPromisedTypeOfPromise(nodeType)) {
return transformThen(node, transformer, outermostParent, prevArgName);
}
else if (isCallExpression(node) && hasPropertyAccessExpressionWithName(node, "catch") && nodeType && !!transformer.checker.getPromisedTypeOfPromise(nodeType)) {
return transformCatch(node, transformer, prevArgName);
}
else if (isPropertyAccessExpression(node)) {
return transformExpression(node.expression, transformer, outermostParent, prevArgName);
}
else if (nodeType && transformer.checker.getPromisedTypeOfPromise(nodeType)) {
return transformPromiseCall(node, transformer, prevArgName);
}
return [];
}
function transformCatch(node: CallExpression, transformer: Transformer, prevArgName?: SynthIdentifier): Statement[] {
const func = node.arguments[0];
const argName = getArgName(func, transformer);
const shouldReturn = transformer.setOfExpressionsToReturn.get(getNodeId(node).toString());
/*
If there is another call in the chain after the .catch() we are transforming, we will need to save the result of both paths (try block and catch block)
To do this, we will need to synthesize a variable that we were not aware of while we were adding identifiers to the synthNamesMap
We will use the prevArgName and then update the synthNamesMap with a new variable name for the next transformation step
*/
if (prevArgName && !shouldReturn) {
prevArgName.numberOfAssignmentsOriginal = 2; // Try block and catch block
transformer.synthNamesMap.forEach((val, key) => {
if (val.identifier.text === prevArgName.identifier.text) {
transformer.synthNamesMap.set(key, getNewNameIfConflict(prevArgName.identifier, transformer.allVarNames));
}
});
// update the constIdentifiers list
if (transformer.constIdentifiers.some(elem => elem.text === prevArgName.identifier.text)) {
transformer.constIdentifiers.push(getNewNameIfConflict(prevArgName.identifier, transformer.allVarNames).identifier);
}
}
const tryBlock = createBlock(transformExpression(node.expression, transformer, node, prevArgName));
const transformationBody = getTransformationBody(func, prevArgName, argName, node, transformer);
const catchArg = argName.identifier.text.length > 0 ? argName.identifier.text : "e";
const catchClause = createCatchClause(catchArg, createBlock(transformationBody));
/*
In order to avoid an implicit any, we will synthesize a type for the declaration using the unions of the types of both paths (try block and catch block)
*/
let varDeclList;
if (prevArgName && !shouldReturn) {
const typeArray: Type[] = prevArgName.types;
const unionType = transformer.checker.getUnionType(typeArray, UnionReduction.Subtype);
const unionTypeNode = transformer.isInJSFile ? undefined : transformer.checker.typeToTypeNode(unionType);
const varDecl = [createVariableDeclaration(getSynthesizedDeepClone(prevArgName.identifier), unionTypeNode)];
varDeclList = createVariableStatement(/*modifiers*/ undefined, createVariableDeclarationList(varDecl, NodeFlags.Let));
}
const tryStatement = createTry(tryBlock, catchClause, /*finallyBlock*/ undefined);
return varDeclList ? [varDeclList, tryStatement] : [tryStatement];
}
function transformThen(node: CallExpression, transformer: Transformer, outermostParent: CallExpression, prevArgName?: SynthIdentifier): Statement[] {
const [res, rej] = node.arguments;
if (!res) {
return transformExpression(node.expression, transformer, outermostParent);
}
const argNameRes = getArgName(res, transformer);
const transformationBody = getTransformationBody(res, prevArgName, argNameRes, node, transformer);
if (rej) {
const argNameRej = getArgName(rej, transformer);
const tryBlock = createBlock(transformExpression(node.expression, transformer, node, argNameRes).concat(transformationBody));
const transformationBody2 = getTransformationBody(rej, prevArgName, argNameRej, node, transformer);
const catchArg = argNameRej.identifier.text.length > 0 ? argNameRej.identifier.text : "e";
const catchClause = createCatchClause(catchArg, createBlock(transformationBody2));
return [createTry(tryBlock, catchClause, /* finallyBlock */ undefined) as Statement];
}
else {
return transformExpression(node.expression, transformer, node, argNameRes).concat(transformationBody);
}
return [];
}
function getFlagOfIdentifier(node: Identifier, constIdentifiers: Identifier[]): NodeFlags {
const inArr: boolean = constIdentifiers.some(elem => elem.text === node.text);
return inArr ? NodeFlags.Const : NodeFlags.Let;
}
function transformPromiseCall(node: Expression, transformer: Transformer, prevArgName?: SynthIdentifier): Statement[] {
const shouldReturn = transformer.setOfExpressionsToReturn.get(getNodeId(node).toString());
// the identifier is empty when the handler (.then()) ignores the argument - In this situation we do not need to save the result of the promise returning call
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
}
else if (!hasPrevArgName && !shouldReturn && (!originalNodeParent || isPropertyAccessExpression(originalNodeParent))) {
return [createStatement(createAwait(node))];
}
return [createReturn(getSynthesizedDeepClone(node))];
}
function createVariableDeclarationOrAssignment(prevArgName: SynthIdentifier, rightHandSide: Expression, transformer: Transformer): NodeArray<Statement> {
if (prevArgName.types.length < prevArgName.numberOfAssignmentsOriginal) {
return createNodeArray([createStatement(createAssignment(getSynthesizedDeepClone(prevArgName.identifier), rightHandSide))]);
}
return createNodeArray([createVariableStatement(/*modifiers*/ undefined,
(createVariableDeclarationList([createVariableDeclaration(getSynthesizedDeepClone(prevArgName.identifier), /*type*/ undefined, rightHandSide)], getFlagOfIdentifier(prevArgName.identifier, transformer.constIdentifiers))))]);
}
function getTransformationBody(func: Node, prevArgName: SynthIdentifier | undefined, argName: SynthIdentifier, parent: CallExpression, transformer: Transformer): NodeArray<Statement> {
const hasPrevArgName = prevArgName && prevArgName.identifier.text.length > 0;
const hasArgName = argName && argName.identifier.text.length > 0;
const shouldReturn = transformer.setOfExpressionsToReturn.get(getNodeId(parent).toString());
switch (func.kind) {
case SyntaxKind.Identifier:
if (!hasArgName) break;
const synthCall = createCall(getSynthesizedDeepClone(func) as Identifier, /*typeArguments*/ undefined, [argName.identifier]);
if (shouldReturn) {
return createNodeArray([createReturn(synthCall)]);
}
if (!hasPrevArgName) break;
const type = transformer.originalTypeMap.get(getNodeId(func).toString());
const callSignatures = type && transformer.checker.getSignaturesOfType(type, SignatureKind.Call);
const returnType = callSignatures && callSignatures[0].getReturnType();
const varDeclOrAssignment = createVariableDeclarationOrAssignment(prevArgName!, createAwait(synthCall), transformer);
prevArgName!.types.push(returnType!);
return varDeclOrAssignment;
case SyntaxKind.FunctionDeclaration:
case SyntaxKind.FunctionExpression:
case SyntaxKind.ArrowFunction:
// Arrow functions with block bodies { } will enter this control flow
if (isFunctionLikeDeclaration(func) && func.body && isBlock(func.body) && func.body.statements) {
let refactoredStmts: Statement[] = [];
for (const statement of func.body.statements) {
if (getReturnStatementsWithPromiseHandlers(statement).length) {
refactoredStmts = refactoredStmts.concat(getInnerTransformationBody(transformer, [statement], prevArgName));
}
else {
refactoredStmts.push(statement);
}
}
return shouldReturn ? getSynthesizedDeepClones(createNodeArray(refactoredStmts)) :
removeReturns(createNodeArray(refactoredStmts), prevArgName!.identifier, transformer.constIdentifiers);
}
else {
const funcBody = (<ArrowFunction>func).body;
const innerRetStmts = getReturnStatementsWithPromiseHandlers(createReturn(funcBody as Expression));
const innerCbBody = getInnerTransformationBody(transformer, innerRetStmts, prevArgName);
if (innerCbBody.length > 0) {
return createNodeArray(innerCbBody);
}
if (hasPrevArgName && !shouldReturn) {
const type = transformer.checker.getTypeAtLocation(func);
const returnType = getLastCallSignature(type, transformer.checker).getReturnType();
const varDeclOrAssignment = createVariableDeclarationOrAssignment(prevArgName!, getSynthesizedDeepClone(funcBody) as Expression, transformer);
prevArgName!.types.push(returnType);
return varDeclOrAssignment;
}
else {
return createNodeArray([createReturn(getSynthesizedDeepClone(funcBody) as Expression)]);
}
}
break;
}
return createNodeArray([]);
}
function getLastCallSignature(type: Type, checker: TypeChecker): Signature {
const callSignatures = type && checker.getSignaturesOfType(type, SignatureKind.Call);
return callSignatures && callSignatures[callSignatures.length - 1];
}
function removeReturns(stmts: NodeArray<Statement>, prevArgName: Identifier, constIdentifiers: Identifier[]): NodeArray<Statement> {
const ret: Statement[] = [];
for (const stmt of stmts) {
if (isReturnStatement(stmt)) {
if (stmt.expression) {
ret.push(createVariableStatement(/*modifiers*/ undefined,
(createVariableDeclarationList([createVariableDeclaration(prevArgName, /*type*/ undefined, stmt.expression)], getFlagOfIdentifier(prevArgName, constIdentifiers)))));
}
}
else {
ret.push(getSynthesizedDeepClone(stmt));
}
}
return createNodeArray(ret);
}
function getInnerTransformationBody(transformer: Transformer, innerRetStmts: Node[], prevArgName?: SynthIdentifier) {
let innerCbBody: Statement[] = [];
for (const stmt of innerRetStmts) {
forEachChild(stmt, function visit(node: Node) {
if (isCallExpression(node)) {
const temp = transformExpression(node, transformer, node, prevArgName);
innerCbBody = innerCbBody.concat(temp);
if (innerCbBody.length > 0) {
return;
}
}
else if (!isFunctionLike(node)) {
forEachChild(node, visit);
}
});
}
return innerCbBody;
}
function hasPropertyAccessExpressionWithName(node: CallExpression, funcName: string): boolean {
if (!isPropertyAccessExpression(node.expression)) {
return false;
}
return node.expression.name.text === funcName;
}
function getArgName(funcNode: Node, transformer: Transformer): SynthIdentifier {
const numberOfAssignmentsOriginal = 0;
const types: Type[] = [];
let name: SynthIdentifier | undefined;
if (isFunctionLikeDeclaration(funcNode)) {
if (funcNode.parameters.length > 0) {
const param = funcNode.parameters[0].name as Identifier;
name = getMapEntryIfExists(param);
}
}
else if (isCallExpression(funcNode) && funcNode.arguments.length > 0 && isIdentifier(funcNode.arguments[0])) {
name = { identifier: funcNode.arguments[0] as Identifier, types, numberOfAssignmentsOriginal };
}
else if (isIdentifier(funcNode)) {
name = getMapEntryIfExists(funcNode);
}
if (!name || name.identifier === undefined || name.identifier.text === "_" || name.identifier.text === "undefined") {
return { identifier: createIdentifier(""), types, numberOfAssignmentsOriginal };
}
return name;
function getMapEntryIfExists(identifier: Identifier): SynthIdentifier {
const originalNode = getOriginalNode(identifier);
const symbol = getSymbol(originalNode);
if (!symbol) {
return { identifier, types, numberOfAssignmentsOriginal };
}
const mapEntry = transformer.synthNamesMap.get(getSymbolId(symbol).toString());
return mapEntry || { identifier, types, numberOfAssignmentsOriginal };
}
function getSymbol(node: Node): Symbol | undefined {
return node.symbol ? node.symbol : transformer.checker.getSymbolAtLocation(node);
}
function getOriginalNode(node: Node): Node {
return node.original ? node.original : node;
}
}
}

View file

@ -3,6 +3,7 @@ namespace ts {
export function computeSuggestionDiagnostics(sourceFile: SourceFile, program: Program, cancellationToken: CancellationToken): DiagnosticWithLocation[] {
program.getSemanticDiagnostics(sourceFile, cancellationToken);
const diags: DiagnosticWithLocation[] = [];
const checker = program.getDiagnosticsProducingTypeChecker();
if (sourceFile.commonJsModuleIndicator &&
(programContainsEs6Modules(program) || compilerOptionsIndicateEs6Modules(program.getCompilerOptions())) &&
@ -68,6 +69,9 @@ namespace ts {
}
}
if (isFunctionLikeDeclaration(node)) {
addConvertToAsyncFunctionDiagnostics(node, checker, diags);
}
node.forEachChild(check);
}
}
@ -109,7 +113,64 @@ namespace ts {
}
}
function addConvertToAsyncFunctionDiagnostics(node: FunctionLikeDeclaration, checker: TypeChecker, diags: DiagnosticWithLocation[]): void {
const functionType = node.type ? checker.getTypeFromTypeNode(node.type) : undefined;
if (isAsyncFunction(node) || !node.body || !functionType) {
return;
}
const callSignatures = checker.getSignaturesOfType(functionType, SignatureKind.Call);
const returnType = callSignatures.length ? checker.getReturnTypeOfSignature(callSignatures[0]) : undefined;
if (!returnType || !checker.getPromisedTypeOfPromise(returnType)) {
return;
}
// collect all the return statements
// check that a property access expression exists in there and that it is a handler
const returnStatements = getReturnStatementsWithPromiseHandlers(node);
if (returnStatements.length > 0) {
diags.push(createDiagnosticForNode(isVariableDeclaration(node.parent) ? node.parent.name : node, Diagnostics.This_may_be_converted_to_an_async_function));
}
}
function getErrorNodeFromCommonJsIndicator(commonJsModuleIndicator: Node): Node {
return isBinaryExpression(commonJsModuleIndicator) ? commonJsModuleIndicator.left : commonJsModuleIndicator;
}
/** @internal */
export function getReturnStatementsWithPromiseHandlers(node: Node): Node[] {
const returnStatements: Node[] = [];
if (isFunctionLike(node)) {
forEachChild(node, visit);
}
else {
visit(node);
}
function visit(child: Node) {
if (isFunctionLike(child)) {
return;
}
if (isReturnStatement(child)) {
forEachChild(child, addHandlers);
}
function addHandlers(returnChild: Node) {
if (isPromiseHandler(returnChild)) {
returnStatements.push(child as ReturnStatement);
}
}
forEachChild(child, visit);
}
return returnStatements;
}
function isPromiseHandler(node: Node): boolean {
return (isCallExpression(node) && isPropertyAccessExpression(node.expression) &&
(node.expression.name.text === "then" || node.expression.name.text === "catch"));
}
}

View file

@ -46,6 +46,7 @@
"codefixes/addMissingInvocationForDecorator.ts",
"codefixes/annotateWithTypeFromJSDoc.ts",
"codefixes/convertFunctionToEs6Class.ts",
"codefixes/convertToAsyncFunction.ts",
"codefixes/convertToEs6Module.ts",
"codefixes/correctQualifiedNameToIndexedAccessType.ts",
"codefixes/fixClassIncorrectlyImplementsInterface.ts",

View file

@ -226,7 +226,7 @@ namespace ts {
export function isJumpStatementTarget(node: Node): node is Identifier & { parent: BreakOrContinueStatement } {
return node.kind === SyntaxKind.Identifier && isBreakOrContinueStatement(node.parent) && node.parent.label === node;
}
}
export function isLabelOfLabeledStatement(node: Node): node is Identifier {
return node.kind === SyntaxKind.Identifier && isLabeledStatement(node.parent) && node.parent.label === node;
@ -396,7 +396,7 @@ namespace ts {
export function isThis(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.ThisKeyword:
// case SyntaxKind.ThisType: TODO: GH#9267
// case SyntaxKind.ThisType: TODO: GH#9267
return true;
case SyntaxKind.Identifier:
// 'this' as a parameter
@ -1656,8 +1656,34 @@ namespace ts {
return clone;
}
function getSynthesizedDeepCloneWorker<T extends Node>(node: T): T {
const visited = visitEachChild(node, getSynthesizedDeepClone, nullTransformationContext);
export function getSynthesizedDeepCloneWithRenames<T extends Node | undefined>(node: T, includeTrivia = true, renameMap?: Map<Identifier>, checker?: TypeChecker, callback?: (originalNode: Node, clone: Node) => any): T {
let clone;
if (node && isIdentifier(node!) && renameMap && checker) {
const symbol = checker.getSymbolAtLocation(node!);
const renameInfo = symbol && renameMap.get(String(getSymbolId(symbol)));
if (renameInfo) {
clone = createIdentifier(renameInfo.text);
}
}
if (!clone) {
clone = node && getSynthesizedDeepCloneWorker(node as NonNullable<T>, renameMap, checker, callback);
}
if (clone && !includeTrivia) suppressLeadingAndTrailingTrivia(clone);
if (callback && node) callback(node!, clone);
return clone as T;
}
function getSynthesizedDeepCloneWorker<T extends Node>(node: T, renameMap?: Map<Identifier>, checker?: TypeChecker, callback?: (originalNode: Node, clone: Node) => any): T {
const visited = (renameMap || checker || callback) ?
visitEachChild(node, wrapper, nullTransformationContext) :
visitEachChild(node, getSynthesizedDeepClone, nullTransformationContext);
if (visited === node) {
// This only happens for leaf nodes - internal nodes always see their children change.
const clone = getSynthesizedClone(node);
@ -1675,6 +1701,10 @@ namespace ts {
// would have made.
visited.parent = undefined!;
return visited;
function wrapper(node: T) {
return getSynthesizedDeepCloneWithRenames(node, /*includeTrivia*/ true, renameMap, checker, callback);
}
}
export function getSynthesizedDeepClones<T extends Node>(nodes: NodeArray<T>, includeTrivia?: boolean): NodeArray<T>;

View file

@ -48,6 +48,7 @@
"unittests/compileOnSave.ts",
"unittests/configurationExtension.ts",
"unittests/convertCompilerOptionsFromJson.ts",
"unittests/convertToAsyncFunction.ts",
"unittests/convertToBase64.ts",
"unittests/convertTypeAcquisitionFromJson.ts",
"unittests/customTransforms.ts",

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,11 @@
// ==ORIGINAL==
/*[#|*/():Promise<void> => {/*|]*/
return fetch('https://typescriptlang.org').then(result => console.log(result));
}
// ==ASYNC FUNCTION::Convert to async function==
async ():Promise<void> => {
const result = await fetch('https://typescriptlang.org');
return console.log(result);
}

View file

@ -0,0 +1,16 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/():Promise<void> {
return fetch('https://typescriptlang.org').then(result => { console.log(result); }).catch(err => { console.log(err); });
}
// ==ASYNC FUNCTION::Convert to async function==
async function f():Promise<void> {
try {
const result = await fetch('https://typescriptlang.org');
console.log(result);
}
catch (err) {
console.log(err);
}
}

View file

@ -0,0 +1,21 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/():Promise<void> {
return fetch('https://typescriptlang.org').then(result => { console.log(result); }, rejection => { console.log("rejected:", rejection); }).catch(err => { console.log(err) });
}
// ==ASYNC FUNCTION::Convert to async function==
async function f():Promise<void> {
try {
try {
const result = await fetch('https://typescriptlang.org');
console.log(result);
}
catch (rejection) {
console.log("rejected:", rejection);
}
}
catch (err) {
console.log(err);
}
}

View file

@ -0,0 +1,39 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/():Promise<void> {
return fetch('https://typescriptlang.org').then(res, rej).catch(catch_err)
}
function res(result){
console.log(result);
}
function rej(rejection){
return rejection.ok;
}
function catch_err(err){
console.log(err);
}
// ==ASYNC FUNCTION::Convert to async function==
async function f():Promise<void> {
try {
try {
const result = await fetch('https://typescriptlang.org');
return res(result);
}
catch (rejection) {
return rej(rejection);
}
}
catch (err) {
return catch_err(err);
}
}
function res(result){
console.log(result);
}
function rej(rejection){
return rejection.ok;
}
function catch_err(err){
console.log(err);
}

View file

@ -0,0 +1,35 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(){
return fetch("https://typescriptlang.org").then(res).catch(rej).then(res);
}
function res(result){
return result;
}
function rej(reject){
return reject;
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(){
let result;
try {
const result_1 = await fetch("https://typescriptlang.org");
result = await res(result_1);
}
catch (reject) {
result = await rej(reject);
}
return res(result);
}
function res(result){
return result;
}
function rej(reject){
return reject;
}

View file

@ -0,0 +1,35 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(){
return fetch("https://typescriptlang.org").then(res).catch(rej).then(res);
}
function res(result){
return result;
}
function rej(reject){
return reject;
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(){
let result: any;
try {
const result_1 = await fetch("https://typescriptlang.org");
result = await res(result_1);
}
catch (reject) {
result = await rej(reject);
}
return res(result);
}
function res(result){
return result;
}
function rej(reject){
return reject;
}

View file

@ -0,0 +1,35 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(){
return fetch("https://typescriptlang.org").then(res).catch(rej).then(res);
}
function res(result): number {
return 5;
}
function rej(reject): number {
return 3;
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(){
let result: number;
try {
const result_1 = await fetch("https://typescriptlang.org");
result = await res(result_1);
}
catch (reject) {
result = await rej(reject);
}
return res(result);
}
function res(result): number {
return 5;
}
function rej(reject): number {
return 3;
}

View file

@ -0,0 +1,35 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(){
return fetch("https://typescriptlang.org").then(res).catch(rej).then(res);
}
function res(result){
return 5;
}
function rej(reject){
return 3;
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(){
let result;
try {
const result_1 = await fetch("https://typescriptlang.org");
result = await res(result_1);
}
catch (reject) {
result = await rej(reject);
}
return res(result);
}
function res(result){
return 5;
}
function rej(reject){
return 3;
}

View file

@ -0,0 +1,35 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(){
return fetch("https://typescriptlang.org").then(res).catch(rej).then(res);
}
function res(result){
return 5;
}
function rej(reject){
return 3;
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(){
let result: number;
try {
const result_1 = await fetch("https://typescriptlang.org");
result = await res(result_1);
}
catch (reject) {
result = await rej(reject);
}
return res(result);
}
function res(result){
return 5;
}
function rej(reject){
return 3;
}

View file

@ -0,0 +1,27 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(){
return fetch("https://typescriptlang.org").then(res => 0).catch(rej => 1).then(res);
}
function res(result): number {
return 5;
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(){
let result: number;
try {
const res = await fetch("https://typescriptlang.org");
result = 0;
}
catch (rej) {
result = 1;
}
return res(result);
}
function res(result): number {
return 5;
}

View file

@ -0,0 +1,27 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(){
return fetch("https://typescriptlang.org").then(res => 0).catch(rej => 1).then(res);
}
function res(result){
return 5;
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(){
let result;
try {
const res = await fetch("https://typescriptlang.org");
result = 0;
}
catch (rej) {
result = 1;
}
return res(result);
}
function res(result){
return 5;
}

View file

@ -0,0 +1,27 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(){
return fetch("https://typescriptlang.org").then(res => 0).catch(rej => 1).then(res);
}
function res(result){
return 5;
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(){
let result: number;
try {
const res = await fetch("https://typescriptlang.org");
result = 0;
}
catch (rej) {
result = 1;
}
return res(result);
}
function res(result){
return 5;
}

View file

@ -0,0 +1,35 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(){
return fetch("https://typescriptlang.org").then(res).catch(rej).then(res);
}
function res(result){
return 5;
}
function rej(reject){
return "Error";
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(){
let result;
try {
const result_1 = await fetch("https://typescriptlang.org");
result = await res(result_1);
}
catch (reject) {
result = await rej(reject);
}
return res(result);
}
function res(result){
return 5;
}
function rej(reject){
return "Error";
}

View file

@ -0,0 +1,35 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(){
return fetch("https://typescriptlang.org").then(res).catch(rej).then(res);
}
function res(result){
return 5;
}
function rej(reject){
return "Error";
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(){
let result: string | number;
try {
const result_1 = await fetch("https://typescriptlang.org");
result = await res(result_1);
}
catch (reject) {
result = await rej(reject);
}
return res(result);
}
function res(result){
return 5;
}
function rej(reject){
return "Error";
}

View file

@ -0,0 +1,35 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(){
return fetch("https://typescriptlang.org").then(res).catch(rej).then(res);
}
function res(result){
return 5;
}
function rej(reject): Response{
return reject;
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(){
let result: number | Response;
try {
const result_1 = await fetch("https://typescriptlang.org");
result = await res(result_1);
}
catch (reject) {
result = await rej(reject);
}
return res(result);
}
function res(result){
return 5;
}
function rej(reject): Response{
return reject;
}

View file

@ -0,0 +1,35 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(){
return fetch("https://typescriptlang.org").then(res).catch(rej).then(res);
}
function res(result){
return 5;
}
function rej(reject){
return reject;
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(){
let result;
try {
const result_1 = await fetch("https://typescriptlang.org");
result = await res(result_1);
}
catch (reject) {
result = await rej(reject);
}
return res(result);
}
function res(result){
return 5;
}
function rej(reject){
return reject;
}

View file

@ -0,0 +1,35 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(){
return fetch("https://typescriptlang.org").then(res).catch(rej).then(res);
}
function res(result){
return 5;
}
function rej(reject){
return reject;
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(){
let result: any;
try {
const result_1 = await fetch("https://typescriptlang.org");
result = await res(result_1);
}
catch (reject) {
result = await rej(reject);
}
return res(result);
}
function res(result){
return 5;
}
function rej(reject){
return reject;
}

View file

@ -0,0 +1,35 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(){
return fetch("https://typescriptlang.org").then(res).catch(rej).then(res);
}
function res(result){
return 5;
}
function rej(reject){
return Promise.resolve(1);
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(){
let result;
try {
const result_1 = await fetch("https://typescriptlang.org");
result = await res(result_1);
}
catch (reject) {
result = await rej(reject);
}
return res(result);
}
function res(result){
return 5;
}
function rej(reject){
return Promise.resolve(1);
}

View file

@ -0,0 +1,35 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(){
return fetch("https://typescriptlang.org").then(res).catch(rej).then(res);
}
function res(result){
return 5;
}
function rej(reject){
return Promise.resolve(1);
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(){
let result: number | Promise<number>;
try {
const result_1 = await fetch("https://typescriptlang.org");
result = await res(result_1);
}
catch (reject) {
result = await rej(reject);
}
return res(result);
}
function res(result){
return 5;
}
function rej(reject){
return Promise.resolve(1);
}

View file

@ -0,0 +1,55 @@
// ==ORIGINAL==
interface a {
name: string;
age: number;
}
interface b extends a {
color: string;
}
function /*[#|*/f/*|]*/(){
return fetch("https://typescriptlang.org").then(res).catch(rej).then(res);
}
function res(result): b{
return {name: "myName", age: 22, color: "red"};
}
function rej(reject): a{
return {name: "myName", age: 27};
}
// ==ASYNC FUNCTION::Convert to async function==
interface a {
name: string;
age: number;
}
interface b extends a {
color: string;
}
async function f(){
let result: a;
try {
const result_1 = await fetch("https://typescriptlang.org");
result = await res(result_1);
}
catch (reject) {
result = await rej(reject);
}
return res(result);
}
function res(result): b{
return {name: "myName", age: 22, color: "red"};
}
function rej(reject): a{
return {name: "myName", age: 27};
}

View file

@ -0,0 +1,16 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/():Promise<void> {
return fetch('https://typescriptlang.org').then(result => console.log(result)).catch(err => console.log(err));
}
// ==ASYNC FUNCTION::Convert to async function==
async function f():Promise<void> {
try {
const result = await fetch('https://typescriptlang.org');
return console.log(result);
}
catch (err) {
return console.log(err);
}
}

View file

@ -0,0 +1,29 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/():Promise<void> {
return fetch('https://typescriptlang.org').then(res).catch(catch_err)
}
function res(result){
console.log(result);
}
function catch_err(err){
console.log(err);
}
// ==ASYNC FUNCTION::Convert to async function==
async function f():Promise<void> {
try {
const result = await fetch('https://typescriptlang.org');
return res(result);
}
catch (err) {
return catch_err(err);
}
}
function res(result){
console.log(result);
}
function catch_err(err){
console.log(err);
}

View file

@ -0,0 +1,33 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(){
var res = 100;
if (res > 50) {
return fetch("https://typescriptlang.org").then(res => console.log(res));
}
else {
return fetch("https://typescriptlang.org").then(res_func);
}
}
function res_func(result){
console.log(result);
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(){
var res = 100;
if (res > 50) {
const res_1 = await fetch("https://typescriptlang.org");
return console.log(res_1);
}
else {
const result = await fetch("https://typescriptlang.org");
return res_func(result);
}
}
function res_func(result){
console.log(result);
}

View file

@ -0,0 +1,33 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(){
var res = 100;
if (res > 50) {
return fetch("https://typescriptlang.org").then(res => console.log(res));
}
else {
return fetch("https://typescriptlang.org").then(res_func);
}
}
function res_func(result){
console.log(result);
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(){
var res = 100;
if (res > 50) {
const res_1 = await fetch("https://typescriptlang.org");
return console.log(res_1);
}
else {
const result = await fetch("https://typescriptlang.org");
return res_func(result);
}
}
function res_func(result){
console.log(result);
}

View file

@ -0,0 +1,34 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(){
return fetch("https://typescriptlang.org").then(res => {
if (res.ok) {
return fetch("https://microsoft.com");
}
else {
if (res.buffer.length > 5) {
return res;
}
else {
return fetch("https://github.com");
}
}
});
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(){
const res = await fetch("https://typescriptlang.org");
if (res.ok) {
return fetch("https://microsoft.com");
}
else {
if (res.buffer.length > 5) {
return res;
}
else {
return fetch("https://github.com");
}
}
}

View file

@ -0,0 +1,34 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(){
return fetch("https://typescriptlang.org").then(res => {
if (res.ok) {
return fetch("https://microsoft.com");
}
else {
if (res.buffer.length > 5) {
return res;
}
else {
return fetch("https://github.com");
}
}
});
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(){
const res = await fetch("https://typescriptlang.org");
if (res.ok) {
return fetch("https://microsoft.com");
}
else {
if (res.buffer.length > 5) {
return res;
}
else {
return fetch("https://github.com");
}
}
}

View file

@ -0,0 +1,11 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(): Promise<void> {
return fetch('https://typescriptlang.org').then( _ => { console.log("done"); });
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(): Promise<void> {
await fetch('https://typescriptlang.org');
console.log("done");
}

View file

@ -0,0 +1,11 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(): Promise<void> {
return fetch('https://typescriptlang.org').then( () => console.log("done") );
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(): Promise<void> {
await fetch('https://typescriptlang.org');
return console.log("done");
}

View file

@ -0,0 +1,19 @@
// ==ORIGINAL==
function /*[#|*/innerPromise/*|]*/(): Promise<string> {
return fetch("https://typescriptlang.org").then(resp => {
var blob2 = resp.blob().then(blob => blob.byteOffset).catch(err => 'Error');
return blob2;
}).then(blob => {
return blob.toString();
});
}
// ==ASYNC FUNCTION::Convert to async function==
async function innerPromise(): Promise<string> {
const resp = await fetch("https://typescriptlang.org");
var blob2 = resp.blob().then(blob => blob.byteOffset).catch(err => 'Error');
const blob_1 = blob2;
return blob_1.toString();
}

View file

@ -0,0 +1,24 @@
// ==ORIGINAL==
function /*[#|*/innerPromise/*|]*/(): Promise<string> {
return fetch("https://typescriptlang.org").then(resp => {
return resp.blob().then(blob => blob.byteOffset).catch(err => 'Error');
}).then(blob => {
return blob.toString();
});
}
// ==ASYNC FUNCTION::Convert to async function==
async function innerPromise(): Promise<string> {
const resp = await fetch("https://typescriptlang.org");
let blob_1: any;
try {
const blob = await resp.blob();
blob_1 = blob.byteOffset;
}
catch (err) {
blob_1 = 'Error';
}
return blob_1.toString();
}

View file

@ -0,0 +1,18 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(): Promise<string> {
return fetch("https://typescriptlang.org").then(resp => {
return resp.blob().then(blob => blob.byteOffset);
}).then(blob => {
return blob.toString();
});
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(): Promise<string> {
const resp = await fetch("https://typescriptlang.org");
const blob = await resp.blob();
const blob_1 = blob.byteOffset;
return blob_1.toString();
}

View file

@ -0,0 +1,17 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(): Promise<string> {
return fetch("https://typescriptlang.org").then(resp => {
var blob = resp.blob().then(blob => blob.byteOffset).catch(err => 'Error');
}).then(blob => {
return blob.toString();
});
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(): Promise<string> {
const resp = await fetch("https://typescriptlang.org");
var blob = resp.blob().then(blob_1 => blob_1.byteOffset).catch(err => 'Error');
return blob_1.toString();
}

View file

@ -0,0 +1,20 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/() {
let x = fetch("https://typescriptlang.org").then(res => console.log(res));
return x.catch(err => console.log("Error!", err));
}
// ==ASYNC FUNCTION::Convert to async function==
async function f() {
let x = fetch("https://typescriptlang.org").then(res => console.log(res));
try {
return x;
}
catch (err) {
return console.log("Error!", err);
}
}

View file

@ -0,0 +1,20 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/() {
let x = fetch("https://typescriptlang.org").then(res => console.log(res));
return x.catch(err => console.log("Error!", err));
}
// ==ASYNC FUNCTION::Convert to async function==
async function f() {
let x = fetch("https://typescriptlang.org").then(res => console.log(res));
try {
return x;
}
catch (err) {
return console.log("Error!", err);
}
}

View file

@ -0,0 +1,16 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(){
return fetch("https://typescriptlang.org").then(res => { for(let i=0; i<10; i++){
console.log(res);
}})
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(){
const res = await fetch("https://typescriptlang.org");
for (let i = 0; i < 10; i++) {
console.log(res);
}
}

View file

@ -0,0 +1,16 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(){
return fetch("https://typescriptlang.org").then(res => { for(let i=0; i<10; i++){
console.log(res);
}})
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(){
const res = await fetch("https://typescriptlang.org");
for (let i = 0; i < 10; i++) {
console.log(res);
}
}

View file

@ -0,0 +1,15 @@
// ==ORIGINAL==
class Parser {
/*[#|*/f/*|]*/():Promise<void> {
return fetch('https://typescriptlang.org').then(result => console.log(result));
}
}
// ==ASYNC FUNCTION::Convert to async function==
class Parser {
async f():Promise<void> {
const result = await fetch('https://typescriptlang.org');
return console.log(result);
}
}

View file

@ -0,0 +1,21 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(): Promise<void> {
return fetch('https://typescriptlang.org').then(res => console.log(res)).catch(err => console.log("err", err)).catch(err2 => console.log("err2", err2));
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(): Promise<void> {
try {
try {
const res = await fetch('https://typescriptlang.org');
return console.log(res);
}
catch (err) {
return console.log("err", err);
}
}
catch (err2) {
return console.log("err2", err2);
}
}

View file

@ -0,0 +1,23 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(): Promise<void> {
let x = fetch("https://microsoft.com").then(res => console.log("Microsoft:", res));
if (x.ok) {
return fetch("https://typescriptlang.org").then(res => console.log(res));
}
return x.then(resp => {
var blob = resp.blob().then(blob => blob.byteOffset).catch(err => 'Error');
});
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(): Promise<void> {
let x = fetch("https://microsoft.com").then(res => console.log("Microsoft:", res));
if (x.ok) {
let res_2 = await fetch("https://typescriptlang.org");
return console.log(res_2);
}
let resp = await x;
var blob = resp.blob().then(blob_1 => blob_1.byteOffset).catch(err => 'Error');
}

View file

@ -0,0 +1,23 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(): Promise<void> {
let x = fetch("https://microsoft.com").then(res => console.log("Microsoft:", res));
if (x.ok) {
return fetch("https://typescriptlang.org").then(res => console.log(res));
}
return x.then(resp => {
var blob = resp.blob().then(blob => blob.byteOffset).catch(err => 'Error');
});
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(): Promise<void> {
let x = fetch("https://microsoft.com").then(res => console.log("Microsoft:", res));
if (x.ok) {
const res_1 = await fetch("https://typescriptlang.org");
return console.log(res_1);
}
const resp = await x;
var blob = resp.blob().then(blob_1 => blob_1.byteOffset).catch(err => 'Error');
}

View file

@ -0,0 +1,26 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(): Promise<void> {
let x = fetch("https://microsoft.com").then(res => console.log("Microsoft:", res));
if (x.ok) {
return fetch("https://typescriptlang.org").then(res => console.log(res));
}
return x.then(resp => {
var blob = resp.blob().then(blob => blob.byteOffset).catch(err => 'Error');
return fetch("https://micorosft.com").then(res => console.log("Another one!"));
});
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(): Promise<void> {
let x = fetch("https://microsoft.com").then(res => console.log("Microsoft:", res));
if (x.ok) {
const res_1 = await fetch("https://typescriptlang.org");
return console.log(res_1);
}
const resp = await x;
var blob = resp.blob().then(blob_1 => blob_1.byteOffset).catch(err => 'Error');
const res_1 = await fetch("https://micorosft.com");
return console.log("Another one!");
}

View file

@ -0,0 +1,24 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/():Promise<void> {
return fetch('https://typescriptlang.org').then(res).then(res2);
}
function res(result){
return result.ok;
}
function res2(result2){
console.log(result2);
}
// ==ASYNC FUNCTION::Convert to async function==
async function f():Promise<void> {
const result = await fetch('https://typescriptlang.org');
const result2 = await res(result);
return res2(result2);
}
function res(result){
return result.ok;
}
function res2(result2){
console.log(result2);
}

View file

@ -0,0 +1,25 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/():Promise<void> {
return fetch('https://typescriptlang.org').then(res).then(res2);
}
function res(result){
return result.ok;
}
function res2(result){
return result.bodyUsed;
}
// ==ASYNC FUNCTION::Convert to async function==
async function f():Promise<void> {
const result = await fetch('https://typescriptlang.org');
const result_1 = await res(result);
return res2(result_1);
}
function res(result){
return result.ok;
}
function res2(result){
return result.bodyUsed;
}

View file

@ -0,0 +1,12 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/():Promise<void> {
return fetch('https://typescriptlang.org').then(result => console.log(result));
}
// ==ASYNC FUNCTION::Convert to async function==
async function f():Promise<void> {
const result = await fetch('https://typescriptlang.org');
return console.log(result);
}

View file

@ -0,0 +1,16 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/():Promise<void | Response> {
return fetch('https://typescriptlang.org').then(null, rejection => console.log("rejected:", rejection));
}
// ==ASYNC FUNCTION::Convert to async function==
async function f():Promise<void | Response> {
try {
await fetch('https://typescriptlang.org');
}
catch (rejection) {
return console.log("rejected:", rejection);
}
}

View file

@ -0,0 +1,16 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/():Promise<void | Response> {
return fetch('https://typescriptlang.org').then(undefined).catch(rej => console.log(rej));
}
// ==ASYNC FUNCTION::Convert to async function==
async function f():Promise<void | Response> {
try {
await fetch('https://typescriptlang.org');
}
catch (rej) {
return console.log(rej);
}
}

View file

@ -0,0 +1,16 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/():Promise<void | Response> {
return fetch('https://typescriptlang.org').catch(rej => console.log(rej));
}
// ==ASYNC FUNCTION::Convert to async function==
async function f():Promise<void | Response> {
try {
return fetch('https://typescriptlang.org');
}
catch (rej) {
return console.log(rej);
}
}

View file

@ -0,0 +1,32 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/() {
return my_print(fetch("https://typescriptlang.org").then(res => console.log(res))).catch(err => console.log("Error!", err));
}
function my_print (resp): Promise<void> {
if (resp.ok) {
console.log(resp.buffer);
}
return resp;
}
// ==ASYNC FUNCTION::Convert to async function==
async function f() {
try {
return my_print(fetch("https://typescriptlang.org").then(res => console.log(res)));
}
catch (err) {
return console.log("Error!", err);
}
}
function my_print (resp): Promise<void> {
if (resp.ok) {
console.log(resp.buffer);
}
return resp;
}

View file

@ -0,0 +1,18 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/() {
return Promise.resolve().then(function () {
return Promise.all([fetch("https://typescriptlang.org"), fetch("https://microsoft.com"), Promise.resolve().then(function () {
return fetch("https://github.com");
}).then(res => res.toString())]);
});
}
// ==ASYNC FUNCTION::Convert to async function==
async function f() {
await Promise.resolve();
return Promise.all([fetch("https://typescriptlang.org"), fetch("https://microsoft.com"), Promise.resolve().then(function() {
return fetch("https://github.com");
}).then(res => res.toString())]);
}

View file

@ -0,0 +1,18 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/() {
return Promise.resolve().then(function () {
return Promise.all([fetch("https://typescriptlang.org"), fetch("https://microsoft.com"), Promise.resolve().then(function () {
return fetch("https://github.com");
}).then(res => res.toString())]);
});
}
// ==ASYNC FUNCTION::Convert to async function==
async function f() {
await Promise.resolve();
return Promise.all([fetch("https://typescriptlang.org"), fetch("https://microsoft.com"), Promise.resolve().then(function() {
return fetch("https://github.com");
}).then(res => res.toString())]);
}

View file

@ -0,0 +1,19 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/() {
return Promise.resolve().then(function () {
return Promise.all([fetch("https://typescriptlang.org"), fetch("https://microsoft.com"), Promise.resolve().then(function () {
return fetch("https://github.com");
})]).then(res => res.toString());
});
}
// ==ASYNC FUNCTION::Convert to async function==
async function f() {
await Promise.resolve();
const res = await Promise.all([fetch("https://typescriptlang.org"), fetch("https://microsoft.com"), Promise.resolve().then(function() {
return fetch("https://github.com");
})]);
return res.toString();
}

View file

@ -0,0 +1,19 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/() {
return Promise.resolve().then(function () {
return Promise.all([fetch("https://typescriptlang.org"), fetch("https://microsoft.com"), Promise.resolve().then(function () {
return fetch("https://github.com");
})]).then(res => res.toString());
});
}
// ==ASYNC FUNCTION::Convert to async function==
async function f() {
await Promise.resolve();
const res = await Promise.all([fetch("https://typescriptlang.org"), fetch("https://microsoft.com"), Promise.resolve().then(function() {
return fetch("https://github.com");
})]);
return res.toString();
}

View file

@ -0,0 +1,18 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/() {
return fetch(Promise.resolve(1).then(res => "https://typescriptlang.org")).catch(err => console.log(err));
}
// ==ASYNC FUNCTION::Convert to async function==
async function f() {
try {
return fetch(Promise.resolve(1).then(res => "https://typescriptlang.org"));
}
catch (err) {
return console.log(err);
}
}

View file

@ -0,0 +1,18 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/() {
return fetch(Promise.resolve(1).then(res => "https://typescriptlang.org")).catch(err => console.log(err));
}
// ==ASYNC FUNCTION::Convert to async function==
async function f() {
try {
return fetch(Promise.resolve(1).then(res => "https://typescriptlang.org"));
}
catch (err) {
return console.log(err);
}
}

View file

@ -0,0 +1,14 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/():Promise<void>{
return Promise.all([fetch('https://typescriptlang.org'), fetch('https://microsoft.com'), fetch('https://youtube.com')]).then(function(vals){
vals.forEach(console.log);
});
}
// ==ASYNC FUNCTION::Convert to async function==
async function f():Promise<void>{
const vals = await Promise.all([fetch('https://typescriptlang.org'), fetch('https://microsoft.com'), fetch('https://youtube.com')]);
vals.forEach(console.log);
}

View file

@ -0,0 +1,17 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/():Promise<void> {
return fetch('https://typescriptlang.org').then(result => { console.log(result); }, rejection => { console.log("rejected:", rejection); });
}
// ==ASYNC FUNCTION::Convert to async function==
async function f():Promise<void> {
try {
const result = await fetch('https://typescriptlang.org');
console.log(result);
}
catch (rejection) {
console.log("rejected:", rejection);
}
}

View file

@ -0,0 +1,17 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/():Promise<void> {
return fetch('https://typescriptlang.org').then(result => console.log(result), rejection => console.log("rejected:", rejection));
}
// ==ASYNC FUNCTION::Convert to async function==
async function f():Promise<void> {
try {
const result = await fetch('https://typescriptlang.org');
return console.log(result);
}
catch (rejection) {
return console.log("rejected:", rejection);
}
}

View file

@ -0,0 +1,29 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/():Promise<void> {
return fetch('https://typescriptlang.org').then(res, rej);
}
function res(result){
console.log(result);
}
function rej(err){
console.log(err);
}
// ==ASYNC FUNCTION::Convert to async function==
async function f():Promise<void> {
try {
const result = await fetch('https://typescriptlang.org');
return res(result);
}
catch (err) {
return rej(err);
}
}
function res(result){
console.log(result);
}
function rej(err){
console.log(err);
}

View file

@ -0,0 +1,18 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/():Promise<boolean> {
return fetch('https://typescriptlang.org').then(res);
}
function res(result){
return result.ok;
}
// ==ASYNC FUNCTION::Convert to async function==
async function f():Promise<boolean> {
const result = await fetch('https://typescriptlang.org');
return res(result);
}
function res(result){
return result.ok;
}

View file

@ -0,0 +1,18 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/():Promise<void> {
return fetch('https://typescriptlang.org').then(res);
}
function res(result){
console.log(result);
}
// ==ASYNC FUNCTION::Convert to async function==
async function f():Promise<void> {
const result = await fetch('https://typescriptlang.org');
return res(result);
}
function res(result){
console.log(result);
}

View file

@ -0,0 +1,17 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/() {
return Promise.resolve().then(() => 1, () => "a");
}
// ==ASYNC FUNCTION::Convert to async function==
async function f() {
try {
await Promise.resolve();
return 1;
}
catch (e) {
return "a";
}
}

View file

@ -0,0 +1,17 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/() {
return Promise.resolve().then(() => 1, () => "a");
}
// ==ASYNC FUNCTION::Convert to async function==
async function f() {
try {
await Promise.resolve();
return 1;
}
catch (e) {
return "a";
}
}

View file

@ -0,0 +1,31 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/() {
var var1:Promise<Response>, var2;
return fetch('https://typescriptlang.org').then( _ =>
Promise.resolve().then( res => {
var2 = "test";
return fetch("https://microsoft.com");
}).then(res =>
var1 === res
)
).then(res);
}
function res(response){
console.log(response);
}
// ==ASYNC FUNCTION::Convert to async function==
async function f() {
var var1:Promise<Response>, var2;
await fetch('https://typescriptlang.org');
let res = await Promise.resolve();
var2 = "test";
let res_1 = fetch("https://microsoft.com");
let response = var1 === res_1;
return res(response);
}
function res(response){
console.log(response);
}

View file

@ -0,0 +1,31 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/() {
var var1:Promise<Response>, var2;
return fetch('https://typescriptlang.org').then( _ =>
Promise.resolve().then( res => {
var2 = "test";
return fetch("https://microsoft.com");
}).then(res =>
var1 === res
)
).then(res);
}
function res(response){
console.log(response);
}
// ==ASYNC FUNCTION::Convert to async function==
async function f() {
var var1:Promise<Response>, var2;
await fetch('https://typescriptlang.org');
const res = await Promise.resolve();
var2 = "test";
const res_1 = fetch("https://microsoft.com");
const response = var1 === res_1;
return res(response);
}
function res(response){
console.log(response);
}

View file

@ -0,0 +1,20 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(){
var i:number;
return fetch("https://typescriptlang.org").then(i => i.ok).then(res => i+1).catch(err => i-1)
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(){
var i:number;
try {
const i_1 = await fetch("https://typescriptlang.org");
const res = i_1.ok;
return i + 1;
}
catch (err) {
return i - 1;
}
}

View file

@ -0,0 +1,24 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/() {
var obj;
return fetch("https://typescriptlang.org").then(function (res) {
obj = {
func: function f() {
console.log(res);
}
};
});
}
// ==ASYNC FUNCTION::Convert to async function==
async function f() {
var obj;
const res = await fetch("https://typescriptlang.org");
obj = {
func: function f() {
console.log(res);
}
};
}

View file

@ -0,0 +1,24 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/() {
var obj;
return fetch("https://typescriptlang.org").then(function (res) {
obj = {
func: function f() {
console.log(res);
}
};
});
}
// ==ASYNC FUNCTION::Convert to async function==
async function f() {
var obj;
const res = await fetch("https://typescriptlang.org");
obj = {
func: function f() {
console.log(res);
}
};
}

View file

@ -0,0 +1,14 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/() {
let i;
return Promise.resolve().then(res => res ? i = res : i = 100);
}
// ==ASYNC FUNCTION::Convert to async function==
async function f() {
let i;
const res = await Promise.resolve();
return res ? i = res : i = 100;
}

View file

@ -0,0 +1,14 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/() {
let i;
return Promise.resolve().then(res => res ? i = res : i = 100);
}
// ==ASYNC FUNCTION::Convert to async function==
async function f() {
let i;
const res = await Promise.resolve();
return res ? i = res : i = 100;
}

View file

@ -0,0 +1,12 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/() {
return Promise.resolve().then(res => console.log(res));
}
// ==ASYNC FUNCTION::Convert to async function==
async function f() {
const res = await Promise.resolve();
return console.log(res);
}

View file

@ -0,0 +1,12 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/() {
return Promise.resolve().then(res => console.log(res));
}
// ==ASYNC FUNCTION::Convert to async function==
async function f() {
const res = await Promise.resolve();
return console.log(res);
}

View file

@ -0,0 +1,11 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(): Promise<void>{
return fetch('https://typescriptlang.org').then(result => { console.log(result) });
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(): Promise<void>{
const result = await fetch('https://typescriptlang.org');
console.log(result);
}

View file

@ -0,0 +1,19 @@
// ==ORIGINAL==
function /*[#|*/f/*|]*/(): Promise<void>{
/* Note - some of these comments are removed during the refactor. This is not ideal. */
// a
/*b*/ return /*c*/ fetch( /*d*/ 'https://typescriptlang.org' /*e*/).then( /*f*/ result /*g*/ => { /*h*/ console.log(/*i*/ result /*j*/) /*k*/}/*l*/);
// m
}
// ==ASYNC FUNCTION::Convert to async function==
async function f(): Promise<void>{
/* Note - some of these comments are removed during the refactor. This is not ideal. */
// a
/*b*/ const result = await fetch(/*d*/ 'https://typescriptlang.org' /*e*/);
console.log(result); /*k*/
// m
}