diff --git a/src/services/codefixes/convertToAsyncFunction.ts b/src/services/codefixes/convertToAsyncFunction.ts index aae1d205de3..3842c30d6aa 100644 --- a/src/services/codefixes/convertToAsyncFunction.ts +++ b/src/services/codefixes/convertToAsyncFunction.ts @@ -14,7 +14,22 @@ namespace ts.codefix { getAllCodeActions: context => codeFixAll(context, errorCodes, (changes, err) => convertToAsyncFunction(changes, err.file, err.start, context.program.getTypeChecker(), context)), }); + const enum SynthBindingNameKind { + Identifier, + BindingPattern, + } + + type SynthBindingName = SynthBindingPattern | SynthIdentifier; + + interface SynthBindingPattern { + readonly kind: SynthBindingNameKind.BindingPattern; + readonly elements: ReadonlyArray; + readonly bindingPattern: BindingPattern; + readonly types: Type[]; + } + interface SynthIdentifier { + readonly kind: SynthBindingNameKind.Identifier; readonly identifier: Identifier; readonly types: Type[]; numberOfAssignmentsOriginal: number; // number of times the variable should be assigned in the refactor @@ -188,8 +203,8 @@ namespace ts.codefix { allVarNames.push({ identifier: synthName.identifier, symbol }); addNameToFrequencyMap(collidingSymbolMap, ident.text, 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))) { + // we only care about identifiers that are parameters, declarations, or binding elements (don't care about other uses) + else if (node.parent && (isParameter(node.parent) || isVariableDeclaration(node.parent) || isBindingElement(node.parent))) { const originalName = node.text; const collidingSymbols = collidingSymbolMap.get(originalName); @@ -204,7 +219,7 @@ namespace ts.codefix { 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*/ }); + synthNamesMap.set(symbolIdString, createSynthIdentifier(identifier, [], 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 }); addNameToFrequencyMap(collidingSymbolMap, originalName, symbol); @@ -261,12 +276,12 @@ namespace ts.codefix { const numVarsSameName = (originalNames.get(name.text) || emptyArray).length; const numberOfAssignmentsOriginal = 0; const identifier = numVarsSameName === 0 ? name : createIdentifier(name.text + "_" + numVarsSameName); - return { identifier, types: [], numberOfAssignmentsOriginal }; + return createSynthIdentifier(identifier, [], numberOfAssignmentsOriginal); } // dispatch function to recursively build the refactoring // should be kept up to date with isFixablePromiseHandler in suggestionDiagnostics.ts - function transformExpression(node: Expression, transformer: Transformer, outermostParent: CallExpression, prevArgName?: SynthIdentifier): ReadonlyArray { + function transformExpression(node: Expression, transformer: Transformer, outermostParent: CallExpression, prevArgName?: SynthBindingName): ReadonlyArray { if (!node) { return emptyArray; } @@ -291,10 +306,11 @@ namespace ts.codefix { return emptyArray; } - function transformCatch(node: CallExpression, transformer: Transformer, prevArgName?: SynthIdentifier): ReadonlyArray { + function transformCatch(node: CallExpression, transformer: Transformer, prevArgName?: SynthBindingName): ReadonlyArray { const func = node.arguments[0]; - const argName = getArgName(func, transformer); + const argName = getArgBindingName(func, transformer); const shouldReturn = transformer.setOfExpressionsToReturn.get(getNodeId(node).toString()); + let possibleNameForVarDecl: SynthIdentifier | undefined; /* 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) @@ -302,67 +318,86 @@ namespace ts.codefix { 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) { - const newSynthName = createUniqueSynthName(prevArgName); - transformer.synthNamesMap.set(key, newSynthName); - } - }); + if (isSynthIdentifier(prevArgName)) { + possibleNameForVarDecl = prevArgName; + transformer.synthNamesMap.forEach((val, key) => { + if (val.identifier.text === prevArgName.identifier.text) { + const newSynthName = createUniqueSynthName(prevArgName); + transformer.synthNamesMap.set(key, newSynthName); + } + }); + } + else { + possibleNameForVarDecl = createSynthIdentifier(createOptimisticUniqueName("result"), prevArgName.types); + } + possibleNameForVarDecl.numberOfAssignmentsOriginal = 2; // Try block and catch block // update the constIdentifiers list - if (transformer.constIdentifiers.some(elem => elem.text === prevArgName.identifier.text)) { - transformer.constIdentifiers.push(createUniqueSynthName(prevArgName).identifier); + if (transformer.constIdentifiers.some(elem => elem.text === possibleNameForVarDecl!.identifier.text)) { + transformer.constIdentifiers.push(createUniqueSynthName(possibleNameForVarDecl).identifier); } } - const tryBlock = createBlock(transformExpression(node.expression, transformer, node, prevArgName)); + const tryBlock = createBlock(transformExpression(node.expression, transformer, node, possibleNameForVarDecl)); - const transformationBody = getTransformationBody(func, prevArgName, argName, node, transformer); - const catchArg = argName ? argName.identifier.text : "e"; - const catchClause = createCatchClause(catchArg, createBlock(transformationBody)); + const transformationBody = getTransformationBody(func, possibleNameForVarDecl, argName, node, transformer); + const catchArg = argName ? isSynthIdentifier(argName) ? argName.identifier.text : argName.bindingPattern : "e"; + const catchVariableDeclaration = createVariableDeclaration(catchArg); + const catchClause = createCatchClause(catchVariableDeclaration, 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; + let varDeclList: VariableStatement | undefined; + let varDeclIdentifier: Identifier | undefined; + if (possibleNameForVarDecl && !shouldReturn) { + varDeclIdentifier = getSynthesizedDeepClone(possibleNameForVarDecl.identifier); + const typeArray: Type[] = possibleNameForVarDecl.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)]; + const varDecl = [createVariableDeclaration(varDeclIdentifier, unionTypeNode)]; varDeclList = createVariableStatement(/*modifiers*/ undefined, createVariableDeclarationList(varDecl, NodeFlags.Let)); } const tryStatement = createTry(tryBlock, catchClause, /*finallyBlock*/ undefined); - return varDeclList ? [varDeclList, tryStatement] : [tryStatement]; + const destructuredResult = prevArgName && varDeclIdentifier && isSynthBindingPattern(prevArgName) + && createVariableStatement(/* modifiers */ undefined, createVariableDeclarationList([createVariableDeclaration(getSynthesizedDeepCloneWithRenames(prevArgName.bindingPattern), /* type */ undefined, varDeclIdentifier)], NodeFlags.Const)); + return compact([varDeclList, tryStatement, destructuredResult]); } - function createUniqueSynthName(prevArgName: SynthIdentifier) { + function getIdentifierTextsFromBindingName(bindingName: BindingName): ReadonlyArray { + if (isIdentifier(bindingName)) return [bindingName.text]; + return flatMap(bindingName.elements, element => { + if (isOmittedExpression(element)) return []; + return getIdentifierTextsFromBindingName(element.name); + }); + } + + function createUniqueSynthName(prevArgName: SynthIdentifier): SynthIdentifier { const renamedPrevArg = createOptimisticUniqueName(prevArgName.identifier.text); - const newSynthName = { identifier: renamedPrevArg, types: [], numberOfAssignmentsOriginal: 0 }; - return newSynthName; + return createSynthIdentifier(renamedPrevArg); } - function transformThen(node: CallExpression, transformer: Transformer, outermostParent: CallExpression, prevArgName?: SynthIdentifier): ReadonlyArray { + function transformThen(node: CallExpression, transformer: Transformer, outermostParent: CallExpression, prevArgName?: SynthBindingName): ReadonlyArray { const [res, rej] = node.arguments; if (!res) { return transformExpression(node.expression, transformer, outermostParent); } - const argNameRes = getArgName(res, transformer); + const argNameRes = getArgBindingName(res, transformer); const transformationBody = getTransformationBody(res, prevArgName, argNameRes, node, transformer); if (rej) { - const argNameRej = getArgName(rej, transformer); + const argNameRej = getArgBindingName(rej, transformer); const tryBlock = createBlock(transformExpression(node.expression, transformer, node, argNameRes).concat(transformationBody)); const transformationBody2 = getTransformationBody(rej, prevArgName, argNameRej, node, transformer); - const catchArg = argNameRej ? argNameRej.identifier.text : "e"; - const catchClause = createCatchClause(catchArg, createBlock(transformationBody2)); + const catchArg = argNameRej ? isSynthIdentifier(argNameRej) ? argNameRej.identifier.text : argNameRej.bindingPattern : "e"; + const catchVariableDeclaration = createVariableDeclaration(catchArg); + const catchClause = createCatchClause(catchVariableDeclaration, createBlock(transformationBody2)); return [createTry(tryBlock, catchClause, /* finallyBlock */ undefined)]; } @@ -370,12 +405,13 @@ namespace ts.codefix { return transformExpression(node.expression, transformer, node, argNameRes).concat(transformationBody); } - function getFlagOfIdentifier(node: Identifier, constIdentifiers: ReadonlyArray): NodeFlags { - const inArr: boolean = constIdentifiers.some(elem => elem.text === node.text); + function getFlagOfBindingName(bindingName: SynthBindingName, constIdentifiers: ReadonlyArray): NodeFlags { + const identifiers = getIdentifierTextsFromBindingName(getNode(bindingName)); + const inArr: boolean = constIdentifiers.some(elem => contains(identifiers, elem.text)); return inArr ? NodeFlags.Const : NodeFlags.Let; } - function transformPromiseCall(node: Expression, transformer: Transformer, prevArgName?: SynthIdentifier): ReadonlyArray { + function transformPromiseCall(node: Expression, transformer: Transformer, prevArgName?: SynthBindingName): ReadonlyArray { 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 originalNodeParent = node.original ? node.original.parent : node.parent; @@ -389,23 +425,23 @@ namespace ts.codefix { return [createReturn(getSynthesizedDeepClone(node))]; } - function createTransformedStatement(prevArgName: SynthIdentifier | undefined, rightHandSide: Expression, transformer: Transformer): ReadonlyArray { - if (!prevArgName || prevArgName.identifier.text.length === 0) { + function createTransformedStatement(prevArgName: SynthBindingName | undefined, rightHandSide: Expression, transformer: Transformer): ReadonlyArray { + if (!prevArgName || isEmpty(prevArgName)) { // if there's no argName to assign to, there still might be side effects return [createStatement(rightHandSide)]; } - if (prevArgName.types.length < prevArgName.numberOfAssignmentsOriginal) { + if (isSynthIdentifier(prevArgName) && prevArgName.types.length < prevArgName.numberOfAssignmentsOriginal) { // if the variable has already been declared, we don't need "let" or "const" return [createStatement(createAssignment(getSynthesizedDeepClone(prevArgName.identifier), rightHandSide))]; } return [createVariableStatement(/*modifiers*/ undefined, - (createVariableDeclarationList([createVariableDeclaration(getSynthesizedDeepClone(prevArgName.identifier), /*type*/ undefined, rightHandSide)], getFlagOfIdentifier(prevArgName.identifier, transformer.constIdentifiers))))]; + (createVariableDeclarationList([createVariableDeclaration(getSynthesizedDeepClone(getNode(prevArgName)), /*type*/ undefined, rightHandSide)], getFlagOfBindingName(prevArgName, transformer.constIdentifiers))))]; } // should be kept up to date with isFixablePromiseArgument in suggestionDiagnostics.ts - function getTransformationBody(func: Expression, prevArgName: SynthIdentifier | undefined, argName: SynthIdentifier | undefined, parent: CallExpression, transformer: Transformer): ReadonlyArray { + function getTransformationBody(func: Expression, prevArgName: SynthBindingName | undefined, argName: SynthBindingName | undefined, parent: CallExpression, transformer: Transformer): ReadonlyArray { const shouldReturn = transformer.setOfExpressionsToReturn.get(getNodeId(parent).toString()); switch (func.kind) { @@ -418,7 +454,7 @@ namespace ts.codefix { break; } - const synthCall = createCall(getSynthesizedDeepClone(func as Identifier), /*typeArguments*/ undefined, [argName.identifier]); + const synthCall = createCall(getSynthesizedDeepClone(func as Identifier), /*typeArguments*/ undefined, isSynthIdentifier(argName) ? [argName.identifier] : []); if (shouldReturn) { return [createReturn(synthCall)]; } @@ -461,7 +497,7 @@ namespace ts.codefix { return shouldReturn ? refactoredStmts.map(s => getSynthesizedDeepClone(s)) : removeReturns( refactoredStmts, - prevArgName === undefined ? undefined : prevArgName.identifier, + prevArgName, transformer, seenReturnStatement); } @@ -503,7 +539,7 @@ namespace ts.codefix { } - function removeReturns(stmts: ReadonlyArray, prevArgName: Identifier | undefined, transformer: Transformer, seenReturnStatement: boolean): ReadonlyArray { + function removeReturns(stmts: ReadonlyArray, prevArgName: SynthBindingName | undefined, transformer: Transformer, seenReturnStatement: boolean): ReadonlyArray { const ret: Statement[] = []; for (const stmt of stmts) { if (isReturnStatement(stmt)) { @@ -514,7 +550,7 @@ namespace ts.codefix { } else { ret.push(createVariableStatement(/*modifiers*/ undefined, - (createVariableDeclarationList([createVariableDeclaration(prevArgName, /*type*/ undefined, possiblyAwaitedExpression)], getFlagOfIdentifier(prevArgName, transformer.constIdentifiers))))); + (createVariableDeclarationList([createVariableDeclaration(getNode(prevArgName), /*type*/ undefined, possiblyAwaitedExpression)], getFlagOfBindingName(prevArgName, transformer.constIdentifiers))))); } } } @@ -526,14 +562,14 @@ namespace ts.codefix { // if block has no return statement, need to define prevArgName as undefined to prevent undeclared variables if (!seenReturnStatement && prevArgName !== undefined) { ret.push(createVariableStatement(/*modifiers*/ undefined, - (createVariableDeclarationList([createVariableDeclaration(prevArgName, /*type*/ undefined, createIdentifier("undefined"))], getFlagOfIdentifier(prevArgName, transformer.constIdentifiers))))); + (createVariableDeclarationList([createVariableDeclaration(getNode(prevArgName), /*type*/ undefined, createIdentifier("undefined"))], getFlagOfBindingName(prevArgName, transformer.constIdentifiers))))); } return ret; } - function getInnerTransformationBody(transformer: Transformer, innerRetStmts: ReadonlyArray, prevArgName?: SynthIdentifier) { + function getInnerTransformationBody(transformer: Transformer, innerRetStmts: ReadonlyArray, prevArgName?: SynthBindingName) { let innerCbBody: Statement[] = []; for (const stmt of innerRetStmts) { @@ -553,17 +589,17 @@ namespace ts.codefix { return innerCbBody; } - function getArgName(funcNode: Expression, transformer: Transformer): SynthIdentifier | undefined { + function getArgBindingName(funcNode: Expression, transformer: Transformer): SynthBindingName | undefined { const numberOfAssignmentsOriginal = 0; const types: Type[] = []; - let name: SynthIdentifier | undefined; + let name: SynthBindingName | undefined; if (isFunctionLikeDeclaration(funcNode)) { if (funcNode.parameters.length > 0) { - const param = funcNode.parameters[0].name as Identifier; - name = getMapEntryOrDefault(param); + const param = funcNode.parameters[0].name; + name = getMappedBindingNameOrDefault(param); } } else if (isIdentifier(funcNode)) { @@ -571,22 +607,32 @@ namespace ts.codefix { } // return undefined argName when arg is null or undefined - if (!name || name.identifier.text === "undefined") { + if (!name || "identifier" in name && name.identifier.text === "undefined") { return undefined; } return name; + function getMappedBindingNameOrDefault(bindingName: BindingName): SynthBindingName { + if (isIdentifier(bindingName)) return getMapEntryOrDefault(bindingName); + const elements = flatMap(bindingName.elements, element => { + if (isOmittedExpression(element)) return []; + return [getMappedBindingNameOrDefault(element.name)]; + }); + + return createSynthBindingPattern(bindingName, elements); + } + function getMapEntryOrDefault(identifier: Identifier): SynthIdentifier { const originalNode = getOriginalNode(identifier); const symbol = getSymbol(originalNode); if (!symbol) { - return { identifier, types, numberOfAssignmentsOriginal }; + return createSynthIdentifier(identifier, types, numberOfAssignmentsOriginal); } const mapEntry = transformer.synthNamesMap.get(getSymbolId(symbol).toString()); - return mapEntry || { identifier, types, numberOfAssignmentsOriginal }; + return mapEntry || createSynthIdentifier(identifier, types, numberOfAssignmentsOriginal); } function getSymbol(node: Node): Symbol | undefined { @@ -597,4 +643,34 @@ namespace ts.codefix { return node.original ? node.original : node; } } + + function isEmpty(bindingName: SynthBindingName | undefined): boolean { + if (!bindingName) { + return true; + } + if (isSynthIdentifier(bindingName)) { + return !bindingName.identifier.text; + } + return every(bindingName.elements, isEmpty); + } + + function getNode(bindingName: SynthBindingName) { + return isSynthIdentifier(bindingName) ? bindingName.identifier : bindingName.bindingPattern; + } + + function createSynthIdentifier(identifier: Identifier, types: Type[] = [], numberOfAssignmentsOriginal = 0): SynthIdentifier { + return { kind: SynthBindingNameKind.Identifier, identifier, types, numberOfAssignmentsOriginal }; + } + + function createSynthBindingPattern(bindingPattern: BindingPattern, elements: ReadonlyArray = emptyArray, types: Type[] = []): SynthBindingPattern { + return { kind: SynthBindingNameKind.BindingPattern, bindingPattern, elements, types }; + } + + function isSynthIdentifier(bindingName: SynthBindingName): bindingName is SynthIdentifier { + return bindingName.kind === SynthBindingNameKind.Identifier; + } + + function isSynthBindingPattern(bindingName: SynthBindingName): bindingName is SynthBindingPattern { + return bindingName.kind === SynthBindingNameKind.BindingPattern; + } } diff --git a/src/services/utilities.ts b/src/services/utilities.ts index 7dc96c10804..5506b45d380 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -1717,7 +1717,19 @@ namespace ts { export function getSynthesizedDeepCloneWithRenames(node: T, includeTrivia = true, renameMap?: Map, checker?: TypeChecker, callback?: (originalNode: Node, clone: Node) => any): T { let clone; - if (isIdentifier(node) && renameMap && checker) { + if (renameMap && checker && isBindingElement(node) && isIdentifier(node.name) && isObjectBindingPattern(node.parent)) { + const symbol = checker.getSymbolAtLocation(node.name); + const renameInfo = symbol && renameMap.get(String(getSymbolId(symbol))); + + if (renameInfo && renameInfo.text !== (node.name || node.propertyName).getText()) { + clone = createBindingElement( + node.dotDotDotToken, + node.propertyName || node.name, + renameInfo, + node.initializer); + } + } + else if (renameMap && checker && isIdentifier(node)) { const symbol = checker.getSymbolAtLocation(node); const renameInfo = symbol && renameMap.get(String(getSymbolId(symbol))); diff --git a/src/testRunner/unittests/services/convertToAsyncFunction.ts b/src/testRunner/unittests/services/convertToAsyncFunction.ts index 8c426effa4c..82a404297a3 100644 --- a/src/testRunner/unittests/services/convertToAsyncFunction.ts +++ b/src/testRunner/unittests/services/convertToAsyncFunction.ts @@ -347,6 +347,24 @@ interface Array {}` _testConvertToAsyncFunction("convertToAsyncFunction_basic", ` function [#|f|](): Promise{ return fetch('https://typescriptlang.org').then(result => { console.log(result) }); +}`); + _testConvertToAsyncFunction("convertToAsyncFunction_arrayBindingPattern", ` +function [#|f|](): Promise{ + return fetch('https://typescriptlang.org').then(([result]) => { console.log(result) }); +}`); + _testConvertToAsyncFunction("convertToAsyncFunction_objectBindingPattern", ` +function [#|f|](): Promise{ + return fetch('https://typescriptlang.org').then(({ result }) => { console.log(result) }); +}`); + _testConvertToAsyncFunction("convertToAsyncFunction_arrayBindingPatternRename", ` +function [#|f|](): Promise{ + const result = getResult(); + return fetch('https://typescriptlang.org').then(([result]) => { console.log(result) }); +}`); + _testConvertToAsyncFunction("convertToAsyncFunction_objectBindingPatternRename", ` +function [#|f|](): Promise{ + const result = getResult(); + return fetch('https://typescriptlang.org').then(({ result }) => { console.log(result) }); }`); _testConvertToAsyncFunction("convertToAsyncFunction_basicNoReturnTypeAnnotation", ` function [#|f|]() { @@ -604,6 +622,50 @@ function [#|innerPromise|](): Promise { ` ); + _testConvertToAsyncFunction("convertToAsyncFunction_InnerPromiseRetBinding1", ` +function [#|innerPromise|](): Promise { + return fetch("https://typescriptlang.org").then(resp => { + return resp.blob().then(({ blob }) => blob.byteOffset).catch(({ message }) => 'Error ' + message); + }).then(blob => { + return blob.toString(); + }); +} +` + ); + + _testConvertToAsyncFunction("convertToAsyncFunction_InnerPromiseRetBinding2", ` +function [#|innerPromise|](): Promise { + return fetch("https://typescriptlang.org").then(resp => { + return resp.blob().then(blob => blob.byteOffset).catch(err => 'Error'); + }).then(({ x }) => { + return x.toString(); + }); +} +` + ); + + _testConvertToAsyncFunction("convertToAsyncFunction_InnerPromiseRetBinding3", ` +function [#|innerPromise|](): Promise { + return fetch("https://typescriptlang.org").then(resp => { + return resp.blob().then(({ blob }) => blob.byteOffset).catch(({ message }) => 'Error ' + message); + }).then(([x, y]) => { + return (x || y).toString(); + }); +} +` + ); + + _testConvertToAsyncFunction("convertToAsyncFunction_InnerPromiseRetBinding4", ` +function [#|innerPromise|](): Promise { + return fetch("https://typescriptlang.org").then(resp => { + return resp.blob().then(({ blob }: { blob: { byteOffset: number } }) => [0, blob.byteOffset]).catch(({ message }: Error) => ['Error ', message]); + }).then(([x, y]) => { + return (x || y).toString(); + }); +} +` + ); + _testConvertToAsyncFunctionFailed("convertToAsyncFunction_VarReturn01", ` function [#|f|]() { let blob = fetch("https://typescriptlang.org").then(resp => console.log(resp)); @@ -1168,7 +1230,13 @@ const { length } = [#|function|] () { _testConvertToAsyncFunction("convertToAsyncFunction_catchBlockUniqueParams", ` function [#|f|]() { - return Promise.resolve().then(x => 1).catch(x => "a").then(x => !!x); + return Promise.resolve().then(x => 1).catch(x => "a").then(x => !!x); +} +`); + + _testConvertToAsyncFunction("convertToAsyncFunction_catchBlockUniqueParamsBindingPattern", ` +function [#|f|]() { + return Promise.resolve().then(() => ({ x: 3 })).catch(() => ({ x: "a" })).then(({ x }) => !!x); } `); diff --git a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_InnerPromiseRetBinding1.ts b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_InnerPromiseRetBinding1.ts new file mode 100644 index 00000000000..7f1559ff7cb --- /dev/null +++ b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_InnerPromiseRetBinding1.ts @@ -0,0 +1,24 @@ +// ==ORIGINAL== + +function /*[#|*/innerPromise/*|]*/(): Promise { + return fetch("https://typescriptlang.org").then(resp => { + return resp.blob().then(({ blob }) => blob.byteOffset).catch(({ message }) => 'Error ' + message); + }).then(blob => { + return blob.toString(); + }); +} + +// ==ASYNC FUNCTION::Convert to async function== + +async function innerPromise(): Promise { + const resp = await fetch("https://typescriptlang.org"); + let blob: any; + try { + const { blob } = await resp.blob(); + blob = blob.byteOffset; + } + catch ({ message }) { + blob = 'Error ' + message; + } + return blob.toString(); +} diff --git a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_InnerPromiseRetBinding2.ts b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_InnerPromiseRetBinding2.ts new file mode 100644 index 00000000000..fb8c32c5efd --- /dev/null +++ b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_InnerPromiseRetBinding2.ts @@ -0,0 +1,25 @@ +// ==ORIGINAL== + +function /*[#|*/innerPromise/*|]*/(): Promise { + return fetch("https://typescriptlang.org").then(resp => { + return resp.blob().then(blob => blob.byteOffset).catch(err => 'Error'); + }).then(({ x }) => { + return x.toString(); + }); +} + +// ==ASYNC FUNCTION::Convert to async function== + +async function innerPromise(): Promise { + const resp = await fetch("https://typescriptlang.org"); + let result: any; + try { + const blob = await resp.blob(); + result = blob.byteOffset; + } + catch (err) { + result = 'Error'; + } + const { x } = result; + return x.toString(); +} diff --git a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_InnerPromiseRetBinding3.ts b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_InnerPromiseRetBinding3.ts new file mode 100644 index 00000000000..f55184aa10f --- /dev/null +++ b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_InnerPromiseRetBinding3.ts @@ -0,0 +1,25 @@ +// ==ORIGINAL== + +function /*[#|*/innerPromise/*|]*/(): Promise { + return fetch("https://typescriptlang.org").then(resp => { + return resp.blob().then(({ blob }) => blob.byteOffset).catch(({ message }) => 'Error ' + message); + }).then(([x, y]) => { + return (x || y).toString(); + }); +} + +// ==ASYNC FUNCTION::Convert to async function== + +async function innerPromise(): Promise { + const resp = await fetch("https://typescriptlang.org"); + let result: any; + try { + const { blob } = await resp.blob(); + result = blob.byteOffset; + } + catch ({ message }) { + result = 'Error ' + message; + } + const [x, y] = result; + return (x || y).toString(); +} diff --git a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_InnerPromiseRetBinding4.ts b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_InnerPromiseRetBinding4.ts new file mode 100644 index 00000000000..e4dff86ffd9 --- /dev/null +++ b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_InnerPromiseRetBinding4.ts @@ -0,0 +1,25 @@ +// ==ORIGINAL== + +function /*[#|*/innerPromise/*|]*/(): Promise { + return fetch("https://typescriptlang.org").then(resp => { + return resp.blob().then(({ blob }: { blob: { byteOffset: number } }) => [0, blob.byteOffset]).catch(({ message }: Error) => ['Error ', message]); + }).then(([x, y]) => { + return (x || y).toString(); + }); +} + +// ==ASYNC FUNCTION::Convert to async function== + +async function innerPromise(): Promise { + const resp = await fetch("https://typescriptlang.org"); + let result: any[]; + try { + const { blob } = await resp.blob(); + result = [0, blob.byteOffset]; + } + catch ({ message }) { + result = ['Error ', message]; + } + const [x, y] = result; + return (x || y).toString(); +} diff --git a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_arrayBindingPattern.ts b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_arrayBindingPattern.ts new file mode 100644 index 00000000000..a2aa01edade --- /dev/null +++ b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_arrayBindingPattern.ts @@ -0,0 +1,11 @@ +// ==ORIGINAL== + +function /*[#|*/f/*|]*/(): Promise{ + return fetch('https://typescriptlang.org').then(([result]) => { console.log(result) }); +} +// ==ASYNC FUNCTION::Convert to async function== + +async function f(): Promise{ + const [result] = await fetch('https://typescriptlang.org'); + console.log(result); +} \ No newline at end of file diff --git a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_arrayBindingPatternRename.ts b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_arrayBindingPatternRename.ts new file mode 100644 index 00000000000..5847e01d57d --- /dev/null +++ b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_arrayBindingPatternRename.ts @@ -0,0 +1,13 @@ +// ==ORIGINAL== + +function /*[#|*/f/*|]*/(): Promise{ + const result = getResult(); + return fetch('https://typescriptlang.org').then(([result]) => { console.log(result) }); +} +// ==ASYNC FUNCTION::Convert to async function== + +async function f(): Promise{ + const result = getResult(); + const [result_1] = await fetch('https://typescriptlang.org'); + console.log(result_1); +} \ No newline at end of file diff --git a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_catchBlockUniqueParams.js b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_catchBlockUniqueParams.js index fa5d3babf72..8bf267e91b5 100644 --- a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_catchBlockUniqueParams.js +++ b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_catchBlockUniqueParams.js @@ -1,13 +1,13 @@ // ==ORIGINAL== function /*[#|*/f/*|]*/() { - return Promise.resolve().then(x => 1).catch(x => "a").then(x => !!x); + return Promise.resolve().then(x => 1).catch(x => "a").then(x => !!x); } // ==ASYNC FUNCTION::Convert to async function== async function f() { - let x_2; + let x_2; try { const x = await Promise.resolve(); x_2 = 1; diff --git a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_catchBlockUniqueParams.ts b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_catchBlockUniqueParams.ts index 15ec7ce93cf..20807ef140c 100644 --- a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_catchBlockUniqueParams.ts +++ b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_catchBlockUniqueParams.ts @@ -1,13 +1,13 @@ // ==ORIGINAL== function /*[#|*/f/*|]*/() { - return Promise.resolve().then(x => 1).catch(x => "a").then(x => !!x); + return Promise.resolve().then(x => 1).catch(x => "a").then(x => !!x); } // ==ASYNC FUNCTION::Convert to async function== async function f() { - let x_2: string | number; + let x_2: string | number; try { const x = await Promise.resolve(); x_2 = 1; diff --git a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_catchBlockUniqueParamsBindingPattern.js b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_catchBlockUniqueParamsBindingPattern.js new file mode 100644 index 00000000000..628a16da1a9 --- /dev/null +++ b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_catchBlockUniqueParamsBindingPattern.js @@ -0,0 +1,20 @@ +// ==ORIGINAL== + +function /*[#|*/f/*|]*/() { + return Promise.resolve().then(() => ({ x: 3 })).catch(() => ({ x: "a" })).then(({ x }) => !!x); +} + +// ==ASYNC FUNCTION::Convert to async function== + +async function f() { + let result; + try { + await Promise.resolve(); + result = ({ x: 3 }); + } + catch (e) { + result = ({ x: "a" }); + } + const { x } = result; + return !!x; +} diff --git a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_catchBlockUniqueParamsBindingPattern.ts b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_catchBlockUniqueParamsBindingPattern.ts new file mode 100644 index 00000000000..ff7ac4bc944 --- /dev/null +++ b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_catchBlockUniqueParamsBindingPattern.ts @@ -0,0 +1,20 @@ +// ==ORIGINAL== + +function /*[#|*/f/*|]*/() { + return Promise.resolve().then(() => ({ x: 3 })).catch(() => ({ x: "a" })).then(({ x }) => !!x); +} + +// ==ASYNC FUNCTION::Convert to async function== + +async function f() { + let result: { x: number; } | { x: string; }; + try { + await Promise.resolve(); + result = ({ x: 3 }); + } + catch (e) { + result = ({ x: "a" }); + } + const { x } = result; + return !!x; +} diff --git a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_objectBindingPattern.ts b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_objectBindingPattern.ts new file mode 100644 index 00000000000..2f5e1991093 --- /dev/null +++ b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_objectBindingPattern.ts @@ -0,0 +1,11 @@ +// ==ORIGINAL== + +function /*[#|*/f/*|]*/(): Promise{ + return fetch('https://typescriptlang.org').then(({ result }) => { console.log(result) }); +} +// ==ASYNC FUNCTION::Convert to async function== + +async function f(): Promise{ + const { result } = await fetch('https://typescriptlang.org'); + console.log(result); +} \ No newline at end of file diff --git a/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_objectBindingPatternRename.ts b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_objectBindingPatternRename.ts new file mode 100644 index 00000000000..98b736f788d --- /dev/null +++ b/tests/baselines/reference/convertToAsyncFunction/convertToAsyncFunction_objectBindingPatternRename.ts @@ -0,0 +1,13 @@ +// ==ORIGINAL== + +function /*[#|*/f/*|]*/(): Promise{ + const result = getResult(); + return fetch('https://typescriptlang.org').then(({ result }) => { console.log(result) }); +} +// ==ASYNC FUNCTION::Convert to async function== + +async function f(): Promise{ + const result = getResult(); + const { result: result_1 } = await fetch('https://typescriptlang.org'); + console.log(result_1); +} \ No newline at end of file