Repository: prettier/prettier
File: src/language-js/needs-parens.js (JavaScript)

Code before:
function shouldAddParenthesesToChainElement(path) {
  if (
    // ESTree
    path.match(
      undefined,
      (node, name) => name === "expression" && node.type === "ChainExpression",
      (node, name) =>
        name === "tag" && node.type === "TaggedTemplateExpression",
    ) ||
    // Babel
    path.match(
      (node) =>
        node.type === "OptionalCallExpression" ||
        node.type === "OptionalMemberExpression",
      (node, name) =>
        name === "tag" && node.type === "TaggedTemplateExpression",
    ) ||
    // case (a?.b)!``; in Babel-ts
    path.match(
      (node) =>
        node.type === "OptionalCallExpression" ||
        node.type === "OptionalMemberExpression",
      (node, name) =>
        name === "expression" &&
        node.type === "TSNonNullExpression" &&
        node?.extra?.parenthesized !== true,
      (node, name) =>
        name === "tag" && node.type === "TaggedTemplateExpression",
    ) ||
    // case (a?.b)!``; in Typescript
    path.match(
      undefined,
      (node, name) => name === "expression" && node.type === "ChainExpression",
      (node, name) =>
        name === "expression" && node.type === "TSNonNullExpression",
      (node, name) =>
        name === "tag" && node.type === "TaggedTemplateExpression",
    ) ||
    // case (a?.b!)``; in Typescript and Babel-ts
    path.match(
      (node) =>
        node.type === "TSNonNullExpression" &&
        node?.extra?.parenthesized === true,
      (node, name) =>
        name === "tag" && node.type === "TaggedTemplateExpression",
    )
  ) {
    return true;
  }

  // Babel, this was implemented before #13735, can use `path.match` as estree does

Review comment (suggestion):
We should avoid this check, we print 

```
(a?.b)!.c;
  
(a?.b!).c;
```

as the same https://prettier.io/playground/#N4Igxg9gdgLgprEAuEAKAhgfgHQCMCUAhNmANwA6UABFZRjrofiaSADQgQAOMAltAGdkodACdREAO4AFMQiEp0AG0noAnkI65R6MAGs4MAMroAtnAAyvKHGQAzZQLhad+w0a67rAc2QxRAK7OIE6mvH6BwXAAHlxworzmsMoAKvFQYrxwCg5KThwCPkpwAIoBEPD2jsEAVgLRRkWl5ZVIufkgAI4tcNISXAog6AIAtDZwACaT7CD+6LxKPgDCEKam6MhDSkozhVDexQCCMP68uAHw0vFWNlV5wQAWMKZKAOoPvPACnmBwRvKfXgAN0+ak2YAEmhAQKCAEkoFNYEYwAkeIcEUYYGpincOlwJE5XjouJt8dl4kDbBxrE5RDA+uhvOtccFPKJaZssXEBCjeDwZvjrDBXrwJjAHsgABwABg4ojg3V48oZTI2bWqHBg6FwIrFEqQACYOAEnCltTkNSA4KZcJMphMLOh9gFGXAAGIQUTrE4+TboC4QEAAXyDQA

The AST is different, but they result the same. We avoid to use the non-standard property.

More details: https://github.com/babel/babel/discussions/15077

Code after:
      (node, name) =>
        name === "tag" && node.type === "TaggedTemplateExpression",
    ) ||
    // Babel-ts
    // (a?.b)!``;
    // (a?.b!)``;
    path.match(
      (node) =>
        node.type === "OptionalCallExpression" ||
        node.type === "OptionalMemberExpression",
      (node, name) =>
        name === "expression" && node.type === "TSNonNullExpression",
      (node, name) =>
        name === "tag" && node.type === "TaggedTemplateExpression",
    ) ||
    // case (a?.b)!``; in Typescript
    path.match(
      undefined,
      (node, name) => name === "expression" && node.type === "ChainExpression",
      (node, name) =>
        name === "expression" && node.type === "TSNonNullExpression",
      (node, name) =>
        name === "tag" && node.type === "TaggedTemplateExpression",
    ) ||
    // case (a?.b!)``; in Typescript
    path.match(
      undefined,
      (node, name) =>
        name === "expression" && node.type === "TSNonNullExpression",
      (node, name) => name === "expression" && node.type === "ChainExpression",
      (node, name) =>
        name === "tag" && node.type === "TaggedTemplateExpression",
    )
  ) {
    return true;
  }

  if (
    path.match(
      (node) =>
        node.type === "OptionalMemberExpression" ||
        node.type === "OptionalCallExpression",
      (node, name) =>
        (name === "object" && node.type === "MemberExpression") ||
        (name === "callee" &&
          (node.type === "CallExpression" || node.type === "NewExpression")),
    ) ||
    path.match(
      (node) =>
        node.type === "OptionalMemberExpression" ||
        node.type === "OptionalCallExpression",