diff --git a/changelog_unreleased/javascript/16391.md b/changelog_unreleased/javascript/16391.md new file mode 100644 index 000000000..738e7bd35 --- /dev/null +++ b/changelog_unreleased/javascript/16391.md @@ -0,0 +1,21 @@ +#### Add parentheses for nullish coalescing in ternary (#16391 by @cdignam-segment) + +This change adds clarity to operator precedence. + + +```js +// Input +foo ? bar ?? foo : baz; +foo ?? bar ? a : b; +a ? b : foo ?? bar; + +// Prettier stable +foo ? bar ?? foo : baz; +foo ?? bar ? a : b; +a ? b : foo ?? bar; + +// Prettier main +foo ? (bar ?? foo) : baz; +(foo ?? bar) ? a : b; +a ? b : (foo ?? bar); +``` diff --git a/src/language-js/needs-parens.js b/src/language-js/needs-parens.js index fbdbece54..4b8c2ac4c 100644 --- a/src/language-js/needs-parens.js +++ b/src/language-js/needs-parens.js @@ -11,6 +11,7 @@ import { isBitwiseOperator, isCallExpression, isMemberExpression, + isNullishCoalescing, isObjectOrRecordExpression, isObjectProperty, shouldFlatten, @@ -374,7 +375,7 @@ function needsParens(path, options) { return !isBinaryCastExpression(node); case "ConditionalExpression": - return isBinaryCastExpression(node); + return isBinaryCastExpression(node) || isNullishCoalescing(node); case "CallExpression": case "NewExpression": diff --git a/src/language-js/utils/index.js b/src/language-js/utils/index.js index 51534145e..3e381180d 100644 --- a/src/language-js/utils/index.js +++ b/src/language-js/utils/index.js @@ -140,6 +140,14 @@ const isObjectOrRecordExpression = createTypeCheckFunction([ "RecordExpression", ]); +/** + * @param {Node} node + * @returns {boolean} + */ +function isNullishCoalescing(node) { + return node.type === "LogicalExpression" && node.operator === "??"; +} + /** * @param {Node} node * @returns {boolean} @@ -1143,6 +1151,7 @@ export { isMemberish, isMethod, isNextLineEmpty, + isNullishCoalescing, isNumericLiteral, isObjectOrRecordExpression, isObjectProperty, diff --git a/tests/format/js/nullish-coalescing/__snapshots__/format.test.js.snap b/tests/format/js/nullish-coalescing/__snapshots__/format.test.js.snap index 1e1bdff6a..cff903059 100644 --- a/tests/format/js/nullish-coalescing/__snapshots__/format.test.js.snap +++ b/tests/format/js/nullish-coalescing/__snapshots__/format.test.js.snap @@ -29,12 +29,16 @@ foo || (baz ?? baz); (foo && baz) ?? baz; foo && (baz ?? baz); +foo ?? bar ? a : b; +a ? foo ?? bar : b; +a ? b : foo ?? bar; + =====================================output===================================== obj.foo ?? "default"; const x = (foo, bar = foo ?? bar) => {}; -foo ? bar ?? foo : baz; +foo ? (bar ?? foo) : baz; foo ?? bar ?? baz; foo ?? bar ?? baz; @@ -53,5 +57,9 @@ foo || (baz ?? baz); (foo && baz) ?? baz; foo && (baz ?? baz); +(foo ?? bar) ? a : b; +a ? (foo ?? bar) : b; +a ? b : (foo ?? bar); + ================================================================================ `; diff --git a/tests/format/js/nullish-coalescing/nullish_coalesing_operator.js b/tests/format/js/nullish-coalescing/nullish_coalesing_operator.js index 327b348b5..4e466ce59 100644 --- a/tests/format/js/nullish-coalescing/nullish_coalesing_operator.js +++ b/tests/format/js/nullish-coalescing/nullish_coalesing_operator.js @@ -20,3 +20,7 @@ foo || (baz ?? baz); (foo && baz) ?? baz; foo && (baz ?? baz); + +foo ?? bar ? a : b; +a ? foo ?? bar : b; +a ? b : foo ?? bar;