diff --git a/changelog_unreleased/typescript/18652.md b/changelog_unreleased/typescript/18652.md new file mode 100644 index 000000000..b57ff1f78 --- /dev/null +++ b/changelog_unreleased/typescript/18652.md @@ -0,0 +1,22 @@ +#### Fix inconsistent formatting of class superClass (#18652 by @fisker) + + +```tsx +// Input +class ALongLongLongLongLongLongLongLongLongLongLongLongClassName + extends foo.bar.Baz {} +class ALongLongLongLongLongLongLongLongLongLongLongLongClassName + extends foo.bar.Baz! {} + +// Prettier stable +class ALongLongLongLongLongLongLongLongLongLongLongLongClassName + extends foo.bar.Baz {} +class ALongLongLongLongLongLongLongLongLongLongLongLongClassName extends (foo + .bar.Baz!) {} + +// Prettier main +class ALongLongLongLongLongLongLongLongLongLongLongLongClassName + extends foo.bar.Baz {} +class ALongLongLongLongLongLongLongLongLongLongLongLongClassName + extends (foo.bar.Baz!) {} +``` diff --git a/src/language-js/parentheses/needs-parentheses.js b/src/language-js/parentheses/needs-parentheses.js index c091aa8ce..99d047156 100644 --- a/src/language-js/parentheses/needs-parentheses.js +++ b/src/language-js/parentheses/needs-parentheses.js @@ -1243,6 +1243,7 @@ function isDecoratorMemberExpression(node) { if (isMemberExpression(node)) { return ( !node.computed && + // @ts-expect-error -- doesn't exists on `MemberExpression` !node.optional && node.property.type === "Identifier" && isDecoratorMemberExpression(node.object) diff --git a/src/language-js/print/class.js b/src/language-js/print/class.js index 8f84e9bb0..13b2f4290 100644 --- a/src/language-js/print/class.js +++ b/src/language-js/print/class.js @@ -168,6 +168,17 @@ function hasMultipleHeritage(node) { return count > 1; } +function isMemberish(node) { + if ( + node?.type === "ChainExpression" || + node?.type === "TSNonNullExpression" + ) { + return isMemberish(node.expression); + } + + return isMemberExpression(node); +} + /** @returns {boolean} */ @@ -187,7 +198,7 @@ function shouldPrintClassInGroupModeWithoutCache(path) { return false; } - return !node.superTypeArguments && isMemberExpression(node.superClass); + return !node.superTypeArguments && isMemberish(node.superClass); } const heritage = diff --git a/src/language-js/print/ternary-old.js b/src/language-js/print/ternary-old.js index e5bb4464d..d6d2ade93 100644 --- a/src/language-js/print/ternary-old.js +++ b/src/language-js/print/ternary-old.js @@ -146,10 +146,11 @@ function shouldExtraIndentForConditionalExpression(path) { const node = path.getParentNode(ancestorCount); if ( - (node.type === "ChainExpression" && node.expression === child) || + ((node.type === "ChainExpression" || + node.type === "TSNonNullExpression") && + node.expression === child) || (isCallExpression(node) && node.callee === child) || - (isMemberExpression(node) && node.object === child) || - (node.type === "TSNonNullExpression" && node.expression === child) + (isMemberExpression(node) && node.object === child) ) { child = node; continue; diff --git a/src/language-js/print/ternary.js b/src/language-js/print/ternary.js index 8ebca3701..597a100d0 100644 --- a/src/language-js/print/ternary.js +++ b/src/language-js/print/ternary.js @@ -103,10 +103,11 @@ function shouldExtraIndentForConditionalExpression(path) { const node = path.getParentNode(ancestorCount); if ( - (node.type === "ChainExpression" && node.expression === child) || + ((node.type === "ChainExpression" || + node.type === "TSNonNullExpression") && + node.expression === child) || (isCallExpression(node) && node.callee === child) || - (isMemberExpression(node) && node.object === child) || - (node.type === "TSNonNullExpression" && node.expression === child) + (isMemberExpression(node) && node.object === child) ) { child = node; continue; diff --git a/src/language-js/utilities/index.js b/src/language-js/utilities/index.js index 9d8323872..fc3658bc5 100644 --- a/src/language-js/utilities/index.js +++ b/src/language-js/utilities/index.js @@ -380,9 +380,10 @@ const isCallExpression = skipChainExpression( createTypeCheckFunction(["CallExpression", "OptionalCallExpression"]), ); -const isMemberExpression = skipChainExpression( - createTypeCheckFunction(["MemberExpression", "OptionalMemberExpression"]), -); +const isMemberExpression = createTypeCheckFunction([ + "MemberExpression", + "OptionalMemberExpression", +]); /** * Attempts to gauge the rough complexity of a node, for example diff --git a/tests/format/typescript/class-and-interface/heritage-break/__snapshots__/format.test.js.snap b/tests/format/typescript/class-and-interface/heritage-break/__snapshots__/format.test.js.snap index 4cae483f3..40638a481 100644 --- a/tests/format/typescript/class-and-interface/heritage-break/__snapshots__/format.test.js.snap +++ b/tests/format/typescript/class-and-interface/heritage-break/__snapshots__/format.test.js.snap @@ -15,6 +15,9 @@ class Short class A_long_long_long_long_long_long_long_long_name12 extends eslint.Rule?.RuleModule {} +class A_long_long_long_long_long_long_long_long_name12 + extends eslint.Rule.RuleModule! {} + interface A_long_long_long_long_long_long_long_long_name2 extends eslint.Rule.RuleModule {} @@ -34,6 +37,9 @@ class Short extends eslint.Rule.RuleModule {} class A_long_long_long_long_long_long_long_long_name12 extends eslint.Rule?.RuleModule {} +class A_long_long_long_long_long_long_long_long_name12 + extends (eslint.Rule.RuleModule!) {} + interface A_long_long_long_long_long_long_long_long_name2 extends eslint.Rule.RuleModule {} diff --git a/tests/format/typescript/class-and-interface/heritage-break/member-expression-like.ts b/tests/format/typescript/class-and-interface/heritage-break/member-expression-like.ts index ed24d9c3f..895a50805 100644 --- a/tests/format/typescript/class-and-interface/heritage-break/member-expression-like.ts +++ b/tests/format/typescript/class-and-interface/heritage-break/member-expression-like.ts @@ -7,6 +7,9 @@ class Short class A_long_long_long_long_long_long_long_long_name12 extends eslint.Rule?.RuleModule {} +class A_long_long_long_long_long_long_long_long_name12 + extends eslint.Rule.RuleModule! {} + interface A_long_long_long_long_long_long_long_long_name2 extends eslint.Rule.RuleModule {}