---
authors: "sosukesuzuki"
title: "Prettier 3.1: New experimental ternaries formatting and Angular control flow syntax!"
---

This release adds indentation back to nested ternaries along with a new `--experimental-ternaries` flag to try a more novel "curious ternary" format that scales better to deeply nested conditionals. We are keen for [your feedback](https://forms.gle/vwEuboCobTVhEkt66) on the experimental format before it rolls out as the default behavior later this year!

We have also added support for the control flow syntax in Angular v17. For details on the syntax, please read [the official Angular release post](https://blog.angular.io/introducing-angular-v17-4d7033312e4b).

<!-- truncate -->

If you appreciate Prettier and would like to support our work, please consider sponsoring us directly via [our OpenCollective](https://opencollective.com/prettier) or by sponsoring the projects we depend on, such as [typescript-eslint](https://opencollective.com/typescript-eslint), [remark](https://opencollective.com/unified), and [Babel](https://opencollective.com/babel). Thank you for your continued support!

## Highlights

### JavaScript

#### Add indentation back to nested ternaries ([#9559](https://github.com/prettier/prettier/pull/9559) by [@rattrayalex](https://github.com/rattrayalex))

<!-- prettier-ignore -->
```js
// Input
const message =
  i % 3 === 0 && i % 5 === 0
    ? "fizzbuzz"
  : i % 3 === 0
    ? "fizz"
  : i % 5 === 0
    ? "buzz"
    : String(i);


// Prettier 3.0
const message =
  i % 3 === 0 && i % 5 === 0
    ? "fizzbuzz"
    : i % 3 === 0
    ? "fizz"
    : i % 5 === 0
    ? "buzz"
    : String(i);

// Prettier 3.1
const message =
  i % 3 === 0 && i % 5 === 0
    ? "fizzbuzz"
    : i % 3 === 0
      ? "fizz"
      : i % 5 === 0
        ? "buzz"
        : String(i);
```

#### New Experimental Ternary Formatting: A Curious Case of the Ternaries ([#13183](https://github.com/prettier/prettier/pull/13183) by [@rattrayalex](https://github.com/rattrayalex))

This is implemented behind a `--experimental-ternaries` flag.

We move the `?` in multiline ternaries to the end of the first line instead of the start of the second, along with several related changes.

While it might look weird at first, beta-testing shows that after a few hours of use, developers find it makes nested ternaries much more readable and useful.

This PR resolves one of our [a highly-upvoted issue](https://github.com/prettier/prettier/issues/5814) without the problems its proposed solution would reintroduce.

Please see [A curious case of the ternaries](https://prettier.io/blog/2023/11/13/curious-ternaries) for more details.

##### Example

<!-- prettier-ignore -->
```js
// "Questioning" ternaries for simple ternaries:
const content =
  children && !isEmptyChildren(children) ?
    render(children)
  : renderDefaultChildren();

// "Case-style" ternaries for chained ternaries:
const message =
  i % 3 === 0 && i % 5 === 0 ? "fizzbuzz"
  : i % 3 === 0 ? "fizz"
  : i % 5 === 0 ? "buzz"
  : String(i);

// Smoothly transitions between "case-style" and "questioning" when things get complicated:
const reactRouterResult =
  children && !isEmptyChildren(children) ? children
  : props.match ?
    component ? React.createElement(component, props)
    : render ? render(props)
    : null
  : null
```

#### Support new syntaxes supported by Babel 7.23.0 ([#15485](https://github.com/prettier/prettier/pull/15485), [#15486](https://github.com/prettier/prettier/pull/15486), [#15487](https://github.com/prettier/prettier/pull/15487), [#15488](https://github.com/prettier/prettier/pull/15488) by [@sosukesuzuki](https://github.com/sosukesuzuki))

We support new JS syntax supported by [Babel 7.23.0](https://babeljs.io/blog/2023/09/25/7.23.0)!

##### Source Phase Imports

Please see https://github.com/tc39/proposal-source-phase-imports for more details.

```js
import source x from "mod";
```

##### Deferred Import Evaluation

Please see https://github.com/tc39/proposal-defer-import-eval for more details.

```js
import defer * as ns from "mod";
```

##### Optional Chaining Assignments

Please see https://github.com/tc39/proposal-optional-chaining-assignment for more details.

```js
maybeObj?.prop1 = value;
```

### Angular

#### Support Angular control flow ([#15606](https://github.com/prettier/prettier/pull/15606) by [@DingWeizhe](https://github.com/DingWeizhe), [@fisker](https://github.com/fisker))

Added support for built-in control flow in Angular 17. Please give us feedback if you find any bugs.

For more details about control flow, please check this article on the official blog.

https://blog.angular.io/introducing-angular-v17-4d7033312e4b

## Other Changes

### JavaScript

#### Fix comment between parentheses and function body ([#15326](https://github.com/prettier/prettier/pull/15326) by [@fisker](https://github.com/fisker))

<!-- prettier-ignore -->
```jsx
// Input
function function_declaration()
// this is a function
{
  return 42
}

(function function_expression()
// this is a function
{
  return 42
})();

// Prettier 3.0
function function_declaration() {
  // this is a function
  return 42;
}

(function function_expression() // this is a function
{
  return 42;
})();

// Prettier 3.1
function function_declaration() {
  // this is a function
  return 42;
}

(function function_expression() {
  // this is a function
  return 42;
})();
```

<!-- prettier-ignore -->
```jsx
// Input
function function_declaration()
// this is a function
{
  return 42
}

export default function()
// this is a function
{
  return 42
}

// Prettier 3.0
TypeError: Cannot read properties of null (reading 'range')

// Prettier 3.1
function function_declaration() {
  // this is a function
  return 42;
}

export default function () {
  // this is a function
  return 42;
}
```

#### Disambiguate unary expressions on left hand side of instanceof and in ([#15468](https://github.com/prettier/prettier/pull/15468) by [@lucacasonato](https://github.com/lucacasonato))

Parentheses are now added around unary expression on the left hand side of
`instanceof` and `in` expressions, to disambiguate the unary on the left hand
side with a unary applying to the entire binary expression.

This helps catch a common mistake where a user intends to write `!("x" in y)`
but instead writes `!"x" in y`, which is really parsed as the nonsensical
`(!"x") in y`.

<!-- prettier-ignore -->
```js
// Input
!"x" in y;
!("x" in y);

// Prettier 3.0
!"x" in y;
!("x" in y);

// Prettier 3.1
(!"x") in y;
!("x" in y);
```

#### Fix name case of selectors in styled components interpolation ([#15472](https://github.com/prettier/prettier/pull/15472) by [@lucasols](https://github.com/lucasols))

<!-- prettier-ignore -->
```js
// Input
const StyledComponent = styled.div`
  margin-right: -4px;

  ${Container}.isExpanded & {
    transform: rotate(-180deg);
  }
`;

const StyledComponent2 = styled.div`
  margin-right: -4px;

  ${abc}.camelCase + ${def}.camelCase & {
    transform: rotate(-180deg);
  }
`;

// Prettier 3.0
const StyledComponent = styled.div`
  margin-right: -4px;

  ${Container}.isexpanded & {
    transform: rotate(-180deg);
  }
`;

const StyledComponent2 = styled.div`
  margin-right: -4px;

  ${abc}.camelcase + ${def}.camelCase & {
    transform: rotate(-180deg);
  }
`;

// Prettier 3.1 -- same as input
```

#### Consistently format strings containing escapes ([#15525](https://github.com/prettier/prettier/pull/15525) by [@sosukesuzuki](https://github.com/sosukesuzuki))

<!-- prettier-ignore -->
```jsx
// Input
export const MSG_GENERIC_OPERATION_FAILURE_BODY_1 =
  goog.getMsg("That's all we know");

export const MSG_GENERIC_OPERATION_FAILURE_BODY_2 =
  goog.getMsg("That\'s all we know");

// Prettier 3.0
export const MSG_GENERIC_OPERATION_FAILURE_BODY_1 =
  goog.getMsg("That's all we know");

export const MSG_GENERIC_OPERATION_FAILURE_BODY_2 = goog.getMsg(
  "That's all we know",
);

// Prettier 3.1
export const MSG_GENERIC_OPERATION_FAILURE_BODY_1 =
  goog.getMsg("That's all we know");

export const MSG_GENERIC_OPERATION_FAILURE_BODY_2 =
  goog.getMsg("That's all we know");
```

#### Improve formatting for assignment its left can break ([#15547](https://github.com/prettier/prettier/pull/15547) by [@sosukesuzuki](https://github.com/sosukesuzuki))

<!-- prettier-ignore -->
```jsx
// Input
params["redirectTo"] =
  `${window.location.pathname}${window.location.search}${window.location.hash}`;

// Prettier 3.0
params[
  "redirectTo"
] = `${window.location.pathname}${window.location.search}${window.location.hash}`;

// Prettier 3.1
params["redirectTo"] =
  `${window.location.pathname}${window.location.search}${window.location.hash}`;

```

### TypeScript

#### Fix unstable comment after the last parameter property ([#15324](https://github.com/prettier/prettier/pull/15324) by [@fisker](https://github.com/fisker))

<!-- prettier-ignore -->
```tsx
// Input
class Class {
  constructor(
      private readonly paramProp: Type,
    // comment
  ) {
  }
}

// Prettier 3.0
class Class {
  constructor(private readonly paramProp: Type) // comment
  {}
}

// Prettier 3.0 (Second format)
class Class {
  constructor(
    private readonly paramProp: Type, // comment
  ) {}
}

// Prettier 3.1
class Class {
  constructor(
    private readonly paramProp: Type,
    // comment
  ) {}
}
```

#### Support embedded formatting in template literals annotated with `as const` ([#15408](https://github.com/prettier/prettier/pull/15408) by [@sosukesuzuki](https://github.com/sosukesuzuki))

<!-- prettier-ignore -->
```tsx
// Input
const GQL_QUERY_WITH_CONST = /* GraphQL */ `
  query S { shop }
` as const;

// Prettier 3.0
const GQL_QUERY_WITH_CONST = /* GraphQL */ `
  query S { shop }
` as const;

// Prettier 3.1
const GQL_QUERY_WITH_CONST = /* GraphQL */ `
  query S {
    shop
  }
` as const;
```

#### Fix printing comment for the last operand of union types ([#15409](https://github.com/prettier/prettier/pull/15409) by [@sosukesuzuki](https://github.com/sosukesuzuki))

<!-- prettier-ignore -->
```tsx
// Input
type Foo1 = (
  | "thing1" // Comment1
  | "thing2" // Comment2
)[]; // Final comment1
type Foo2 = (
  | "thing1" // Comment1
  | "thing2" // Comment2
) & Bar; // Final comment2

// Prettier 3.0
type Foo1 = (
  | "thing1" // Comment1
  | "thing2"
)[]; // Comment2 // Final comment1
type Foo2 = (
  | "thing1" // Comment1
  | "thing2"
) & // Comment2
  Bar; // Final comment2

// Prettier 3.1
type Foo1 = (
  | "thing1" // Comment1
  | "thing2" // Comment2
)[]; // Final comment1
type Foo2 = (
  | "thing1" // Comment1
  | "thing2" // Comment2
) &
  Bar; // Final comment2
```

#### Keep required parenthesis around some specific keyword like identifiers in expression statement of satisfies / as expression ([#15514](https://github.com/prettier/prettier/pull/15514) by [@seiyab](https://github.com/seiyab))

<!-- prettier-ignore -->
```ts
// Input
(type) satisfies never;


// Prettier 3.0
type satisfies never;


// Prettier 3.1
(type) satisfies never;
```

### Flow

#### Support `as` and `satisfies` expressions for Flow ([#15130](https://github.com/prettier/prettier/pull/15130) by [@gkz](https://github.com/gkz))

<!-- prettier-ignore -->
```flow
// Input
const x = y as T;

// Prettier 3.0
// <error: unsupported>

// Prettier 3.1
const x = y as T;
```

#### Support type arguments on jsx opening elements for Flow ([#15429](https://github.com/prettier/prettier/pull/15429) by [@SamChou19815](https://github.com/SamChou19815))

<!-- prettier-ignore -->
```jsx
// Input
<Foo<bar> />;

// Prettier 3.0
<Foo />;

// Prettier 3.1
<Foo<bar> />;
```

#### Support type arguments after `typeof` ([#15466](https://github.com/prettier/prettier/pull/15466) by [@sosukesuzuki](https://github.com/sosukesuzuki))

Supports type arguments after `typeof` syntax supported since [Flow v0.127.0](https://github.com/facebook/flow/blob/HEAD/Changelog.md#02170):

<!-- prettier-ignore -->
```jsx
type Foo = typeof MyGenericClass<string, number>;
```

### SCSS

#### Do not split call of scss function with leading dash ([#15370](https://github.com/prettier/prettier/pull/15370) by [@auvred](https://github.com/auvred))

<!-- prettier-ignore -->
```scss
/* Input */
div {
  width: -double(-double(3));
}

/* Prettier 3.0 */
div {
  width: -double(- double(3));
}

/* Prettier 3.1 */
div {
  width: -double(-double(3));
}
```

### HTML

#### Fix formatting of `menu` and `marquee` elements ([#15334](https://github.com/prettier/prettier/pull/15334) by [@fisker](https://github.com/fisker))

<!-- prettier-ignore -->
```html
<!-- Input -->
<menu><li><button onclick="copy()">Copy</button></li>
  <li><button onclick="cut()">Cut</button></li>
  <li><button onclick="paste()">Paste</button></li></menu>
<marquee
  direction="down"
  width="250"
  height="200"
  behavior="alternate"
  style="border:solid"><marquee behavior="alternate"> This text will bounce </marquee></marquee>

<!-- Prettier 3.0 -->
<menu
  ><li><button onclick="copy()">Copy</button></li>
  <li><button onclick="cut()">Cut</button></li>
  <li><button onclick="paste()">Paste</button></li></menu
>
<marquee
  direction="down"
  width="250"
  height="200"
  behavior="alternate"
  style="border: solid"
  ><marquee behavior="alternate"> This text will bounce </marquee></marquee
>

<!-- Prettier 3.1 -->
<menu>
  <li><button onclick="copy()">Copy</button></li>
  <li><button onclick="cut()">Cut</button></li>
  <li><button onclick="paste()">Paste</button></li>
</menu>
<marquee
  direction="down"
  width="250"
  height="200"
  behavior="alternate"
  style="border: solid"
>
  <marquee behavior="alternate">This text will bounce</marquee>
</marquee>
```

### Markdown

#### Encoding `<` and `>` in markdown urls ([#15400](https://github.com/prettier/prettier/pull/15400) by [@vivekjoshi556](https://github.com/vivekjoshi556))

<!-- prettier-ignore -->
```md
<!-- Input -->
[link](https://www.google.fr/()foo->bar)

<!-- Prettier 3.0 -->
[link](<https://www.google.fr/()foo->bar>)

<!-- Prettier 3.1 -->
[link](<https://www.google.fr/()foo-%3Ebar>)
```

<!-- prettier-ignore -->
```md
<!-- Input -->
![link](<https://www.google.fr/()foo->bar>)

<!-- Prettier 3.0 -->
![link](<https://www.google.fr/()foo->bar>)

<!-- Prettier 3.1 -->
![link](<https://www.google.fr/()foo-%3Ebar>)
```

#### Don't split lines between Japanese kana & COMBINING KATAKANA-HIRAGANA (SEMI-)VOICED SOUND MARK ([#15411](https://github.com/prettier/prettier/pull/15411) by [@tats-u](https://github.com/tats-u))

This PR fixes [#15410](https://github.com/prettier/prettier/issues/15410).

Japanese (semi-)voiced kana characters can be split into two code points. For example, the following hiragana character /ka/ can be represented as:

が (U+304C) → か (U+304B) + ゙ (U+3099) → が (U+304C U+3099)

Most users do not use or meet expressions like this except for file paths in macOS. However, there are some characters that can only be represented in this way. Some Japanese text that have to tell /ŋa̠/ (there are not a few Japanese that do not use it these days though) from the common /ga/ use a expression ["か゚" (U+304B U+309A)](https://en.wiktionary.org/wiki/%E3%81%8B%E3%82%9A).

```markdown
nasalか゚き゚く゚け゚こ゚か゚き゚く゚け゚こ゚か゚き゚く゚け゚こ゚か゚き゚く゚け゚こ゚
```

The above Markdown is formatted as in Prettier 3.0:

```markdown
nasalか゚き゚く゚け゚こ゚か゚き゚く゚け゚こ゚か゚き゚く゚け゚こ゚か゚き゚く゚け
゚こ゚
```

The semi-voiced sound mark goes to the next line but it is not correct. By this PR, the source Markdown is now formatted as:

```markdown
nasalか゚き゚く゚け゚こ゚か゚き゚く゚け゚こ゚か゚き゚く゚け゚こ゚か゚き゚く゚
け゚こ゚
```

The semi-voiced sound mark now keeps attached to the hiragana "け".

### API

#### Accept `URL` in `prettier.{resolveConfig,resolveConfigFile,getFileInfo}()` ([#15332](https://github.com/prettier/prettier/pull/15332), [#15354](https://github.com/prettier/prettier/pull/15354), [#15360](https://github.com/prettier/prettier/pull/15360), [#15364](https://github.com/prettier/prettier/pull/15364) by [@fisker](https://github.com/fisker))

[`prettier.resolveConfig()`](https://prettier.io/docs/api#prettierresolveconfigfileurlorpath--options), [`prettier.resolveConfigFile()`](https://prettier.io/docs/api#prettierresolveconfigfilefileurlorpath), and [`prettier.getFileInfo()`](https://prettier.io/docs/api#prettiergetfileinfofileurlorpath--options) now accepts an [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL) with `file:` protocol or a url string starts with `file://`.

```js
// `URL`
await prettier.resolveConfig(new URL("./path/to/file", import.meta.url));
await prettier.resolveConfigFile(new URL("./path/to/file", import.meta.url));
await prettier.getFileInfo(new URL("./path/to/file", import.meta.url));
await prettier.getFileInfo("/path/to/file", {
  ignorePath: new URL("./.eslintignore", import.meta.url),
});

// URL string
await prettier.resolveConfig("file:///path/to/file");
await prettier.resolveConfigFile("file:///path/to/file");
await prettier.getFileInfo("file:///path/to/file");
await prettier.getFileInfo("/path/to/file", {
  ignorePath: "file:///path/to/.eslintignore",
});
```

### CLI

#### Process files only supported by plugins ([#15433](https://github.com/prettier/prettier/pull/15433) by [@sosukesuzuki](https://github.com/sosukesuzuki))

In Prettier 3.0, when specifying a directory from the CLI, only files with default supported extensions were processed.

In the following scenario, not just `foo.js` but also `foo.astro` should be formatted:

```
# Prettier 3.0 version

$ ls .
foo.js foo.astro

$ cat .prettierrc
{ "plugins": ["prettier-plugin-astro"] }

$ prettier --write .
foo.js 20ms
```

With this update, both `foo.js` and `foo.astro` will now be formatted:

```
# Prettier 3.1 branch

$ prettier --write .
foo.js 20ms
foo.astro 32ms
```

You can replace `prettier "**/*" --ignore-unknown` with `prettier .` since they are equivalent now.

#### Show `(unchanged)` keyword for accessibility in CLI `--write` ([#15467](https://github.com/prettier/prettier/pull/15467) by [@ADTC](https://github.com/ADTC))

Previously, the only distinction between a changed file and an unchanged file was the grey color of the file name. In the example below, we can't distinguish between `a.js` and `b.js` as the color is missing. This issue is fixed by adding the `(unchanged)` keyword which makes the distinction accessible without color.

<!-- prettier-ignore -->
```sh
prettier --write .

# Prettier 3.0
a.js 0ms
b.js 0ms
c.js 0ms (cached)

# Prettier 3.1
a.js 0ms
b.js 0ms (unchanged)
c.js 0ms (unchanged) (cached)
```

#### Fix error when formatting file names contains special characters ([#15597](https://github.com/prettier/prettier/pull/15597) by [@fisker](https://github.com/fisker))

```sh
prettier "[with-square-brackets].js" --list

# Prettier 3.0
[error] Explicitly specified file was ignored due to negative glob patterns: "[with-square-brackets].js".

# Prettier 3.1
[with-square-brackets].js
```
