diff --git a/changelog_unreleased/typescript/20027.md b/changelog_unreleased/typescript/20027.md new file mode 100644 index 000000000..35ee62717 --- /dev/null +++ b/changelog_unreleased/typescript/20027.md @@ -0,0 +1,16 @@ +#### Add missing parentheses for `TSInstantiationExpression` (#20027 by @fisker) + + +```tsx +// Input +new (a?.b)(); +(a?.b)`foo`; + +// Prettier stable +new a?.b(); +a?.b`foo`; + +// Prettier main +new (a?.b)(); +(a?.b)`foo`; +``` diff --git a/src/language-js/parentheses/chain-expression.js b/src/language-js/parentheses/chain-expression.js index f0b113b2c..061e658da 100644 --- a/src/language-js/parentheses/chain-expression.js +++ b/src/language-js/parentheses/chain-expression.js @@ -8,7 +8,6 @@ function shouldAddParenthesesToChainExpression(path) { const { key, parent } = path; return ( - (key === "expression" && parent.type === "TSNonNullExpression") || (key === "object" && parent.type === "MemberExpression" && !parent.optional) || @@ -16,7 +15,8 @@ function shouldAddParenthesesToChainExpression(path) { parent.type === "CallExpression" && !parent.optional) || (key === "callee" && parent.type === "NewExpression") || - (key === "tag" && parent.type === "TaggedTemplateExpression") + (key === "tag" && parent.type === "TaggedTemplateExpression") || + (key === "expression" && parent.type === "TSNonNullExpression") ); } @@ -56,14 +56,17 @@ function isBabelOptionalChainRoot(path) { ); } +function isChainExpressionRoot(path) { + return path.node.type === "ChainExpression" || isBabelOptionalChainRoot(path); +} + /** @param {AstPath} path @returns {boolean} */ function shouldAddParenthesesToChainElement(path) { return ( - (path.node.type === "ChainExpression" || isBabelOptionalChainRoot(path)) && - shouldAddParenthesesToChainExpression(path) + isChainExpressionRoot(path) && shouldAddParenthesesToChainExpression(path) ); } diff --git a/src/language-js/parentheses/needs-parentheses.js b/src/language-js/parentheses/needs-parentheses.js index 84a1a8dc4..196a78b07 100644 --- a/src/language-js/parentheses/needs-parentheses.js +++ b/src/language-js/parentheses/needs-parentheses.js @@ -878,7 +878,32 @@ function needsParentheses(path, options) { ); case "TSInstantiationExpression": - return key === "object" && isMemberExpression(parent); + /* + There are cases not really needed parentheses + - `(a)()` + - `new (A)()` + - ``` (a)`` ``` + - `class A extends (A) {}` + + However, `typescript-eslint` produces different AST, + we simply add parentheses without checking the shape of the expression. + + See https://github.com/typescript-eslint/typescript-eslint/issues/12863 + See https://github.com/typescript-eslint/typescript-eslint/issues/12012 + */ + if ( + (key === "object" && isMemberExpression(parent)) || + (key === "callee" && parent.type === "CallExpression") || + (key === "callee" && parent.type === "NewExpression") || + (key === "tag" && parent.type === "TaggedTemplateExpression") || + (key === "superClass" && + (parent.type === "ClassDeclaration" || + parent.type === "ClassExpression")) + ) { + return true; + } + + break; case "MatchOrPattern": return parent.type === "MatchAsPattern"; diff --git a/tests/format/typescript/chain-expression/__snapshots__/format.test.js.snap b/tests/format/typescript/chain-expression/__snapshots__/format.test.js.snap index d04ecb6e9..114640bd0 100644 --- a/tests/format/typescript/chain-expression/__snapshots__/format.test.js.snap +++ b/tests/format/typescript/chain-expression/__snapshots__/format.test.js.snap @@ -76,6 +76,14 @@ foo((a?.b)!); (a?.b!()).c?.(); (a?.b())!.c?.(); +// \`TSInstantiationExpression\` +a(); +(a)(); +(a?.b); +(a?.b); +(a?.b!); +(a?.b)!; + =====================================output===================================== // Member expressions (a?.b)!(); @@ -148,6 +156,14 @@ a?.b()!?.(); (a?.b!()).c?.(); (a?.b())!.c?.(); +// \`TSInstantiationExpression\` +a(); +(a)(); +a?.b; +a?.b; +a?.b!; +(a?.b)!; + ================================================================================ `; @@ -543,6 +559,18 @@ new Foo(a?.b!); new ((a?.b!)?.c)(); new ((a?.b)!?.c)(); +// \`TSInstantiationExpression\` +new a(); +new (a)(); +new (a.b)(); +new (a.b)(); +new (a?.b)(); +new (a?.b)(); +new ((a?.b)!)(); +new ((a?.b)!)(); +new ((a?.b!))(); +new ((a?.b!))(); + =====================================output===================================== // Member expressions new (a?.b)!(); @@ -613,6 +641,18 @@ new Foo(a?.b!); new (a?.b!?.c)(); new ((a?.b)!?.c)(); +// \`TSInstantiationExpression\` +new a(); +new (a)(); +new (a.b)(); +new a.b(); +new (a?.b)(); +new (a?.b)(); +new ((a?.b)!)(); +new (a?.b)!(); +new (a?.b!)(); +new (a?.b!)(); + ================================================================================ `; @@ -687,6 +727,18 @@ parsers: ["typescript"] ((a?.b!)?.c)\`\`; ((a?.b)!?.c)\`\`; +// \`TSInstantiationExpression\` +a\`\`; +(a)\`\`; +(a.b)\`\`; +(a.b)\`\`; +(a?.b)\`\`; +(a?.b)\`\`; +((a?.b)!)\`\`; +((a?.b)!)\`\`; +((a?.b!))\`\`; +((a?.b!))\`\`; + =====================================output===================================== // Member expressions (a?.b)!\`\`; @@ -754,6 +806,18 @@ a(b?.c!)\`\`; (a?.b!?.c)\`\`; ((a?.b)!?.c)\`\`; +// \`TSInstantiationExpression\` +a\`\`; +(a)\`\`; +(a.b)\`\`; +a.b\`\`; +(a?.b)\`\`; +(a?.b)\`\`; +((a?.b)!)\`\`; +(a?.b)!\`\`; +(a?.b!)\`\`; +(a?.b!)\`\`; + ================================================================================ `; diff --git a/tests/format/typescript/chain-expression/call-expression.ts b/tests/format/typescript/chain-expression/call-expression.ts index a74a477cb..caace46aa 100644 --- a/tests/format/typescript/chain-expression/call-expression.ts +++ b/tests/format/typescript/chain-expression/call-expression.ts @@ -68,3 +68,11 @@ foo((a?.b)!); (a?.b())!?.(); (a?.b!()).c?.(); (a?.b())!.c?.(); + +// `TSInstantiationExpression` +a(); +(a)(); +(a?.b); +(a?.b); +(a?.b!); +(a?.b)!; diff --git a/tests/format/typescript/chain-expression/new-expression.ts b/tests/format/typescript/chain-expression/new-expression.ts index 275c4735e..5442a8efa 100644 --- a/tests/format/typescript/chain-expression/new-expression.ts +++ b/tests/format/typescript/chain-expression/new-expression.ts @@ -66,3 +66,15 @@ new Foo(a?.b!); // In optional chaining new ((a?.b!)?.c)(); new ((a?.b)!?.c)(); + +// `TSInstantiationExpression` +new a(); +new (a)(); +new (a.b)(); +new (a.b)(); +new (a?.b)(); +new (a?.b)(); +new ((a?.b)!)(); +new ((a?.b)!)(); +new ((a?.b!))(); +new ((a?.b!))(); diff --git a/tests/format/typescript/chain-expression/tagged-template-literals.ts b/tests/format/typescript/chain-expression/tagged-template-literals.ts index a18bb2d6f..2d1cba283 100644 --- a/tests/format/typescript/chain-expression/tagged-template-literals.ts +++ b/tests/format/typescript/chain-expression/tagged-template-literals.ts @@ -63,3 +63,15 @@ // In optional chaining ((a?.b!)?.c)``; ((a?.b)!?.c)``; + +// `TSInstantiationExpression` +a``; +(a)``; +(a.b)``; +(a.b)``; +(a?.b)``; +(a?.b)``; +((a?.b)!)``; +((a?.b)!)``; +((a?.b!))``; +((a?.b!))``; diff --git a/tests/format/typescript/instantiation-expression/__snapshots__/format.test.js.snap b/tests/format/typescript/instantiation-expression/__snapshots__/format.test.js.snap index 3b6bf749b..46a338627 100644 --- a/tests/format/typescript/instantiation-expression/__snapshots__/format.test.js.snap +++ b/tests/format/typescript/instantiation-expression/__snapshots__/format.test.js.snap @@ -150,6 +150,35 @@ a[(Array).name]; ================================================================================ `; +exports[`super-class.ts format 1`] = ` +====================================options===================================== +parsers: ["typescript"] + printWidth: 80 (default) | +=====================================input====================================== + class ClassDeclaration_1 extends B {} +( class ClassExpresssion_1 extends B {} ); + class ClassDeclaration_2 extends (B) {} +( class ClassExpresssion_2 extends (B) {} ); + +// This not really make sense +// https://github.com/typescript-eslint/typescript-eslint/issues/12012 + class ClassDeclaration_3 extends (B) {} +( class ClassExpresssion_3 extends (B) {} ); + +=====================================output===================================== +class ClassDeclaration_1 extends B {} +(class ClassExpresssion_1 extends B {}); +class ClassDeclaration_2 extends (B) {} +(class ClassExpresssion_2 extends (B) {}); + +// This not really make sense +// https://github.com/typescript-eslint/typescript-eslint/issues/12012 +class ClassDeclaration_3 extends (B) {} +(class ClassExpresssion_3 extends (B) {}); + +================================================================================ +`; + exports[`typeof.ts format 1`] = ` ====================================options===================================== parsers: ["typescript"] diff --git a/tests/format/typescript/instantiation-expression/super-class.ts b/tests/format/typescript/instantiation-expression/super-class.ts new file mode 100644 index 000000000..62d876338 --- /dev/null +++ b/tests/format/typescript/instantiation-expression/super-class.ts @@ -0,0 +1,9 @@ + class ClassDeclaration_1 extends B {} +( class ClassExpresssion_1 extends B {} ); + class ClassDeclaration_2 extends (B) {} +( class ClassExpresssion_2 extends (B) {} ); + +// This not really make sense +// https://github.com/typescript-eslint/typescript-eslint/issues/12012 + class ClassDeclaration_3 extends (B) {} +( class ClassExpresssion_3 extends (B) {} );