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

Code before:
            case "BindExpression":
              object = object.object;
              break;
            // tagged templates are basically member expressions from a grammar perspective
            // see https://tc39.github.io/ecma262/#prod-MemberExpression
            case "TaggedTemplateExpression":
              object = object.tag;
              break;
            case "TSNonNullExpression":
              object = object.expression;
              break;
            default:
              return false;
          }
        }
      }

      if (
        parent.type === "TaggedTemplateExpression" &&
        (node.type === "OptionalMemberExpression" ||
          node.type === "OptionalCallExpression")
      ) {
        return true;
      }

      if (
        parent.type === "ChainExpression" &&
        path.grandparent.type === "TaggedTemplateExpression"
      ) {
        let object = node;

        do {
          if (object?.optional === true) {
            return true;
          }

          object =
            object.type === "CallExpression" ? object.callee : object.object;
        } while (object);
      }

      return false;

    case "BindExpression":
      return (
        (key === "callee" &&
          (parent.type === "BindExpression" ||
            parent.type === "NewExpression")) ||
        (key === "object" && isMemberExpression(parent))
      );
    case "NGPipeExpression":

Review comment (suggestion):
I don't think this block is needed, simple `node.type === "ChainExpressin"` should be enough.

Code after:
            case "BindExpression":
              object = object.object;
              break;
            // tagged templates are basically member expressions from a grammar perspective
            // see https://tc39.github.io/ecma262/#prod-MemberExpression
            case "TaggedTemplateExpression":
              object = object.tag;
              break;
            case "TSNonNullExpression":
              object = object.expression;
              break;
            default:
              return false;
          }
        }
      }

      return false;

    case "BindExpression":
      return (
        (key === "callee" &&
          (parent.type === "BindExpression" ||
            parent.type === "NewExpression")) ||
        (key === "object" && isMemberExpression(parent))
      );
    case "NGPipeExpression":
      if (
        parent.type === "NGRoot" ||
        parent.type === "NGMicrosyntaxExpression" ||
        (parent.type === "ObjectProperty" &&
          // Preserve parens for compatibility with AngularJS expressions
          !node.extra?.parenthesized) ||
        isArrayOrTupleExpression(parent) ||
        (key === "arguments" && isCallExpression(parent)) ||
        (key === "right" && parent.type === "NGPipeExpression") ||
        (key === "property" && parent.type === "MemberExpression") ||
        parent.type === "AssignmentExpression"
      ) {
        return false;
      }
      return true;
    case "JSXFragment":
    case "JSXElement":
      return (
        key === "callee" ||
        (key === "left" &&
          parent.type === "BinaryExpression" &&
          parent.operator === "<") ||
        (!isArrayOrTupleExpression(parent) &&
          parent.type !== "ArrowFunctionExpression" &&