Prettier: Keep multi-line formatting for destructuring assignment

Created on 27 Jul 2017  ·  107Comments  ·  Source: prettier/prettier

I think multi-line destructuring should not be collapsed by Prettier.

Same concept as the multi-line objects special case.

When doing destructuring (particularly nested) new lines can greatly enhance readability.

Example of current behaviour
Input:

// Object with multiple lines uncollapsed by Prettier
const user = {
  name: {
    first: "Zoid",
    last: "Berg",
  },
};

// Similar destructuring assignment is collapsed by Prettier
const {
  name: {
    first,
    last,
  },
} = user;

Prettier output:

// Object with multiple lines uncollapsed by Prettier
const user = {
  name: {
    first: "Zoid",
    last: "Berg"
  }
};

// Similar destructuring assignment is collapsed by Prettier
const { name: { first, last } } = user;
destructuring javascript needs discussion

Most helpful comment

I agree that the above example is quite readable, I was only going for a minimal example case.

For larger and deeper destructuring assignments I find it harder to read.

Larger example
Input:

const {
  name: {
    first,
    last,
  },
  organisation: {
    address: {
      street: orgStreetAddress,
      postcode: orgPostcode,
    },
  },
} = user;

Prettier output:

const {
  name: { first, last },
  organisation: { address: { street: orgStreetAddress, postcode: orgPostcode } }
} = user;

If there's no interest in this change then I'm fine to drop it.
I found it a bit of a barrier to adoption at work where we have some very big props destructuring in React components and I thought others might have the same issue.

All 107 comments

The output seems perfectly readable to me.

The special-casing of objects is more of a failing in prettier than a feature, I don't think it makes sense to spread that to more cases.

I agree that the above example is quite readable, I was only going for a minimal example case.

For larger and deeper destructuring assignments I find it harder to read.

Larger example
Input:

const {
  name: {
    first,
    last,
  },
  organisation: {
    address: {
      street: orgStreetAddress,
      postcode: orgPostcode,
    },
  },
} = user;

Prettier output:

const {
  name: { first, last },
  organisation: { address: { street: orgStreetAddress, postcode: orgPostcode } }
} = user;

If there's no interest in this change then I'm fine to drop it.
I found it a bit of a barrier to adoption at work where we have some very big props destructuring in React components and I thought others might have the same issue.

Agreed, the input looks better.

Maybe a better rule would be "always break nested object patterns onto multiple lines", rather than looking at the source. Though that rule might have to only apply to variable declarations, else this will be an issue:

function f({ data: { name } }) {}
//Out:
function f({
  data: {
    name
  }
}) {}

Which would likely be seen as a regression.

For React pure render functions I do find myself writing them similarly to your example output above.
Each destructed variable is on a new line.

export default UserComponent ({
  name: {
    first,
    last,
  },
  organisation: {
    address: {
      street: orgStreetAddress,
      postcode: orgPostcode,
    },
  },
}) {
  return (
    <div>
      ...
    </div>
  )
};

I'd be fine with destructuring always using multiple lines but I imagine many others won't share that opinion.

Are you against referencing the source formatting because it's difficult or because it's a special case and against the general spirit of Prettier?

Are you against referencing the source formatting because it's difficult or because it's a special case and against the general spirit of Prettier?

The latter. But moreso because it makes prettier less predictable. Having to learn "tricks" to get prettier to print optimal code sucks. An example I run into all the time:

Object.assign({
  foo: bar
}, baz);

Will be printed as

Object.assign(
  {
    foo: bar
  },
  baz
);

When I usually want

Object.assign(
  { foo: bar },
  baz
);

Which you can convince prettier to do by removing the line breaks, but not everyone knows that.

@Brian-Gaffney makes an excelent point, I use that destructuring a lot on react pure components and I'm a little tired of using // prettier-ignore plus I found myself on this scenario:

when you add or remove a key the diff will look like

import {
  Foo,
  Bar,
-  Baz, 
}

but with prettier the hole line has changes

- import { Foo, Bar, Baz } from './Qux'
+ import { Foo, Bar } from './Qux'

PS - Going for the minimal example here but keep in consideration real use cases like
{ name: { first, last, }, organisation: { address: { street: orgStreetAddress, postcode: orgPostcode, }, }, }

I just opened up #2685 as a dupe of this.

I'll reiterate my stance from that here.

I would expect them to be treated the same - either both collapse, or both stay expanded.
(Personally I would prefer both to be expanded, combined with #2068 being a thing you'd invoke sometimes)

Same with ES6 named imports, FWIW.

Maybe we can limit multi-line formatting to only destructuring statements with 2 or more props (possibly also make it configurable)? Similar to the ESLint minProperties option on their multi-line object rules (https://eslint.org/docs/rules/object-curly-newline#minproperties). This would still ignore existing formatting so prettier can format things in a consistent way.

I'd love to have similar (configurable) behavior for objects, arrays and function args too.

@alexrqs yep, diff-friendly code style is a good thing.

Not to sound overeager, but these sound like good reasons to me. Next I'd like to add a trailing comma.

What do we need to do to keep the discussion moving forward. Perhaps it can be weaved into #74 ? Is it right to suggest that this format would be consistent with the way we (want to) treat object literals and lists?

https://github.com/prettier/prettier/issues/15#issuecomment-271681495

If you also have less/css files in mind, consider scenarios like these:

body,div,dl,dt,dd,ul,ol,li,
h1,h2,h3,h4,h5,h6,
pre,code,form,fieldset,legend,input,textarea,p,blockquote,
th,td,hr,button,article,aside,details,figcaption,figure,
footer,header,hgroup,menu,nav,section {
  margin: 0;
  padding: 0;
}

Nobody want every of those selectors on a separate line. Which currently happens. (and using // prettier-ignore in too many places is no fun, either). Is there a way to generally suppress this for .less-files?

A linefeed right before printWidth gets reached (before more comma-separated selectors on the next line) would be good... or an option to completely leave them alone (as you can see above, those linefeeds kinda structure them, i.e. all headline-sectors together...)

@nocke Your example is not very good for suggesting different formatting. You only do that reset _once,_ right? So it doesn't matter that much if that one rule gets a 20-line selector (in my opinion).

Is the point of prettier to allow teams to agree on and enforce a style mechanically? Or is it to export the authors' particular preferences to the world at large?

If the former, then I think it would benefit one of:

  • Respecting the original intent as the object syntax formatting does
  • Using a max-item-count for both objects and destructure statements
  • Allowing a character-count limit for single-line objects and destructure statement separate from line width

This is a big sticking point with some members of my team - I'm personally willing to pay the price of a few ugly destructures in order to get mechanical formatting, but I don't write the cheques.

is it to export the authors' particular preferences to the world at large?

Definitely not that one. Sometimes issues need a _lot_ of discussions before the best solution to go forward is found. Like the JSX ternary change. And sometime everyone ends up working on bugs or other features instead.

Maybe a better rule would be "always break nested object patterns onto multiple lines", rather than looking at the source. Though that rule might have to only apply to variable declarations - @azz

I'm in favor of this- that said, I never use nested destructuring, partially because I prefer to keep prop shapes pretty shallow, and partially because I find it difficult to grok.

2 real-world javascript examples:

A) There are times, where you just want things as a big blob (my first example) –not wasting vertical space to scroll over– that's hardly ever to change:

const npmCommands = [
    'access', 'add-user', 'adduser', 'apihelp', 'author', 'bin', 'bugs', 'c',
    'cache', 'completion', 'config', 'ddp', 'dedupe', 'deprecate', 'dist-tag',
    'dist-tags', 'docs', 'edit', 'explore', 'faq', 'find', 'find-dupes', 'get',
    'unlink', 'unpublish', 'unstar', 'up', 'update', 'upgrade', 'v', 'version',
    'view', 'whoami' ];

B) ...in the very same project there are places, where you want everything on it's own line for sake of maintainability, even if it amounts to dozens of lines:

const slugify: HashMap = {
    '£': 'pound',
    '¥': 'yen',
    'ß': 'ss',
    'à': 'a',
    'á': 'a',
    'â': 'a',
    'ã': 'a',
    'ä': 'ae',
    ...

Meaning:
An auto-mode that detects (no rocket since, rather simple: are there „mostly“ linebreaks between the object/array members/css selectors or not?) and then reinforces that on any minor inconsistencies... (as prettier should do).

As a side note, ESLint has options to allow breaking these nested objects into multiple lines vs. keeping them consolidated.

At the minimum, it seems that prettier should be configurable to accept consolidated or broken-apart nested objects since the best style seem tricky to define except on a case-by-case basis.

At the minimum, it seems that prettier should be configurable to accept consolidated or broken-apart
nested objects since the best style seem tricky to define except on a case-by-case basis.

I would see (an option for) auto-detection as a good case-by-case basis. (see above) Do not rely on length or any other assumption, rely an what's there (and then consolidate, if the apparently chosen style is only „mostly“ there...).

By the way, js-beautify has something like that, appending a ,preserve-inline

"brace_style": "collapse,preserve-inline",

tested, and it works pretty nicely...

Regarding:

Meaning:
An auto-mode that detects (no rocket since, rather simple: are there „mostly“ linebreaks between the object/array members/css selectors or not?) and then reinforces that on any minor inconsistencies... (as prettier should do).

and:

I would see (an option for) auto-detection as a good case-by-case basis. (see above) Do not rely on length or any other assumption, rely an what's there (and then consolidate, if the apparently chosen style is only „mostly“ there...).

I'm not in any way affiliated with the project, but based on my understanding of it that would be impossible. Specifically I'm referring to the "detects ... are there mostly linebreaks between ..." part and the "rely on what's there" part. As I understand it prettier reduces your code to an AST first; that's part of the "magic" of how it can format files consistently. And doing that amounts to discarding all formatting that was there previously.

In other words, Prettier can't do anything based on the way you had formatted something, because it is designed to throw out everything you had before and just generate a consistent result.

I think the real fix here would be to give users more control over how Prettier output things _every time_. That is a request I'd definitely :+1:

In other words, Prettier can't do anything based on the way you had formatted something, because it is designed to throw out everything you had before and just generate a consistent result.

That’s not entirely true — Prettier does have ways of accessing the original source, and that’s used to keep multline objects multiline and preserve (single) blank lines. There is a feature request somewhere for an option to disable this and only use the AST, though.

There is a feature request somewhere for an option to disable this and only use the AST, though.

Here it is, for reference: https://github.com/prettier/prettier/issues/2068

As a new user testing Prettier in a fresh project, the newline behaviour around all destructuring (including import statements) and arrays, along with the closely related issues regarding jsx #3101 and json #2716, are the only deal-breakers preventing adoption.

Our own reasons for using multi-line formatting in imports, destructures, objects, arrays, etc. relate mainly to supporting simple / automatic re-ordering and easy addition and removal of props, all of which help maintain clean git history (i.e. when used with trailingComma).

As mentioned in https://github.com/prettier/prettier/issues/3101#issuecomment-357739674 code reviews benefit from multi-line props - this extends to merge operations - and the same applies when destructuring.

Without this configuration option, it's possible we would see _more_, not less, formatting related changes in our git history which is at odds with our own motivations for implementing Prettier.

FWIW the only time I _wouldn't_ prefer a multi-line destructure is in an inline arrow function such as:

[
  {
    key: 'value',
  },
].map(({ key }) => key);

...but given the choice I would rather have the option to _always_ enforce newlines when destructuring than support edge cases like this.

The running suggestion in https://github.com/prettier/prettier/issues/3101#issuecomment-355279030 of defining the max number of props per line seems like it could carry across for all destructuring and import statements as well. i.e. a value of 0 would always enforce multi-line, 1 allows for a single inline destructure (as in example above), and 3 inline props seems to be the magic number for those not attached to blanket enforcement. Could this be combined with another option to respect intent as per https://github.com/prettier/prettier/issues/2550#issuecomment-348442621 ?


As an aside, the current destructure formatting seems inconsistent with the formatting applied when constructing an object- which appears to respect the author's intent. i.e. the following passes through formatting as-is:

const a = 0;
const b = 1;
const c = { a, b };
const d = {
  a,
  b,
};

(although this is not the case with arrays...)

Could we just _always_ break onto multiple lines if there's nested destructuring, and keep it as it is for flat destructuring?

If it warrants a configuration option I'd love to see a setting that supports always breaking.

@azz to illustrate my preference, compare the following diffs:

# Breaking nested destructure only
-const { a, b, c } = someObject;
+const {
+  a,
+  b,
+  c,
+  d: {
+    e,
+  },
+} = someObject;
# Always breaking (clear addition, minimal delta)
const {
   a,
   b,
   c,
+  d: {
+    e,
+  },
 } = someObject;

Meanwhile changes in another branch now need to be merged with the nested destructure formatting - each case results in a conflict - compare the two diffs:

# Breaking nested destructure only (conflict must be resolved manually)
-const {
-  a,
-  b,
-  c,
-  d: {
-    e,
-  },
-} = someObject;
+const { a, b, c, f, g } = someObject;
# Always breaking (easily resolved with merge tool / editor by accepting both changes)
const {
   a,
   b,
   c,
-  d: {
-    e,
-  },
+  f,
+  g,
 } = someObject;

We can't _always_ optimize for diffs. Imagine if we printed _every_ function like this:

function foo(
  a,
  b
) {}

We don't want to do that, and your example is similar. e.g.:

const {
  a,
} = b;

// or

const f = ({
  a,
}) => a;

I like having pretty much everything as expanded as is possible, my method to get prettier to do this was to set printWidth really low. Mine is at 20. This means it expands nearly everything as much as possible.
Maybe the feature isn't needed and the desired result can be achieved by lowering the printWidth

I'd really like to use Prettier _AND_ have readable code, which as I understand it is the goal of this project.

The current destructuring output is just atrocious. Not only is it harder to read, but it makes debugging difficult. I can't simply comment out a property now. I have to delete it, painstakingly use \* *\ style comments instead of a single hotkey; or manually expand the code.

Can we please get resolution on this? It's a serious problem with an otherwise excellent tool.

For now I've turned off Prettier because of this. It's only a hassle until these multi-line issues are no longer forced on users. I understand this is an "opinionated" formatted, but if there's no options to make the project usable, this project will probably not keep many users.

Since this has been open for nearly 8 months without comment from the owner, I am guessing this issue will not be resolved.

Since this has been open for nearly 8 months without comment from the owner, I am guessing this issue will not be resolved.

This is not true, while James is is author of this project, there are many maintainers (collaborators) capable of resolving the problem.

What we are lacking is a heuristic to use to satisfy this problem without introducing style regressions. To help with this, would you be able to share snippets of code that look bad when Prettier formats them?

@azz you're right about the specific point on the comment from owner. I should not have made this point. That being said,the issue has been open for 8 months and the idea, while being discussed, it, doesn't seem to have much support from the maintainers of this library.

It's not simply a matter of code that looks bad. It's that this makes the code hard to work with. That being said, any import of a sufficient number of properties is going to look bad.

    const {
      property1,
      property2,
      property3,
      property4,
      property5
    } = this.props;

Becomes >>

    const { property1, property2, property3, property4, property5 } = this.props;

The readability here is lacking only slightly. The much bigger problem with this is that the code is not pliable in this form. If I want to comment out an item, this is now kind of a chore. During a refactor of older/dirtier code, you may often want to comment out these items instead of deleting them for easy reversibility.

    const {
      property1,
      property2,
      property3,
      // property4,
      property5
    } = this.props;

@Slapbox you can use the online prettier playground here: https://prettier.io/playground/

The "Copy markdown" button in the bottom right is very useful when sharing results on GitHub.

@suchipi thanks I actually did use that, but I forgot to update my text.

Can a maintainer please signify some forward momentum on this? Multi-line destructuring deserves a thought out response. Desctructuring nested objects is indeed not pliable and is kind of a chore to read.

It's the only reason we do not use prettier on my team.

Everyone says that in every issue :)

@dlombardi - Sometimes its good to just try things out for a while and suppress judgement, and acknowledge that it can take getting used to. All of the preferences that come with having written hand-crafted code over the course of a career are tough to let go of, but if you can manage it its shocking how much friction Prettier removes, especially with VSCode formatOnSave turned on. (How much time did I used to spend moving copy / paste code around? Now cmd+s does all that for me, instantly. Its amazing.)

I was way, way on the fence due to destructuring and multi-line JSX props and a few other things but once I gave it an honest spin over a substantial codebase I can't believe I ever lived without this tool. You'd be doing your team a great favor by implementing it rather than blocking, or at least taking it for a longer test-run. I can almost guarantee that your issue with being able to comment out destructured props will very quickly fade as you get into the flow of things and start understanding just now much it assists (and speeds up) the code-writing process.

@dmassi I think that was an excellent defense of all of Prettiers lack of customization, not just destructuring objects, and we both agree that Prettier is really great.

However, I disagree that with the idea that (if you'll forgive me for re-stating what you said in a crass way) "there's nothing wrong with Prettier, the problem is you for not liking how Prettier does things, so just adopt Prettier and the problem (ie. you) will be solved." And I also don't think something being good is an argument against making it better.

Prettier would be a better library for everyone if it offered more customization options in general, and one for multi-line object properties in particular. And I say this as someone who has suppressed judgement, and has used the library despite it doing things I'm uncomfortable with.

Is my life better with Prettier, in spite of those uncomfortable things? 100%, I'm not going back. But that's not the same thing as saying Prettier is the best tool possible and adding more customization would make it worse.

As an outsider looking in at the lack of progress on tickets like this one it certainly _feels_ like the lack of customization comes from resistance from the maintainers. If that's true it's unfortunate, because no one developer or software team knows what is right (or even what is right modulo Prettier's current customization options) for every other developer/team.

@machineghost

I invite you to read our docs section about our philosophy on options which answers your post more in depth.

@duailibe I've read it, and I feel my post speaks to its deficiencies. Prettier is two things conflated: an _incredible_ formatting a tool, and an opinion on how everyone should write their code. My opinion is that it could be a more incredible tool by easing up on the idea that Prettier's maintainers know how everyone's code should be written. It could still be a tool that strongly encourages people to write it "The Prettier Team Way" ... without prohibiting alternatives.

If the real desire is to codify a standard then Prettier can _easily_ offer one ... while still allowing shops to customize differently from that standard if they desire. Babel offers presets, and that's great as they put every Babel-using developer on the same page. But Babel certainly doesn't try and stop anyone from adopting a module that's not in that preset, and I don't think "The Javascript Code Formatting Tool" should be any different from "The Javascript Transpiling Tool".

Not to be obtuse but I've still not heard a rationale behind the current destructuring method and why it is not instead multi-line.

Event better,

Could we just always break onto multiple lines if there's nested destructuring, and keep it as it is for flat destructuring?

Every JSON formatter prints structures in multi-line formats for what I assume is for its readability. Same kind of formatting is done for GraphQL queries. If destructuring is effectively getting (setting variables to) data from an object shouldn't we have the same consistent standard that these popular tools encourage?

Having now put in 2 PRs into this code base... this thing is complex. If there were more flags I cannot fathom how difficult it would be to maintain. It would certainly be hard for an outsider like me to make a contribution.

I would like to see better heuristics on this too, but now I am thinking about complexity, and what would those rules look like? What are all the edge cases?

I really like the original code in https://github.com/prettier/prettier/issues/2550#issuecomment-318354352

That said, if the property names matched the variables, I would probably want it like this:

const {
  name: { first, last },
  organisation: { 
    address: { street, postcode }
  }
} = user;

Anyway, the only way forward is to make concrete proposals. Complaining will accomplish nothing.

So, if we put rules to it (I'm going to call these objects , but I also mean destructured object definitions).

An object is "simple" if any are true:

  • It has less than N property shortcut entries only
  • It has less than M property/variable pairs only (lets make mixed shortcut and pairs "complex")

In the case of object construction, this would also mean that computed values imply the wrapping object is complex.

An array is "simple" if any are true:

  • It has less than O values and those values are all variables
  • It has 1 value (computed or not) and that value fits on a single line

Rules:

  • If an object literal is simple, single line
  • Otherwise its multiline

This matches my above example.

It would convert:

const {
  name: {
    first,
    last,
  },
  organisation: {
    address: {
      street: orgStreetAddress,
      postcode: orgPostcode,
    },
  },
} = user;

into

const {
  name: { first, last },
  organisation: {
    address: {
      street: orgStreetAddress,
      postcode: orgPostcode,
    },
  },
} = user;

where N === 5, M === 2

I think this is doable and works for both destructuring and object literals.

If N, M, and O are configurable, then the people who want everything broken per line get their way.

Lets discuss proposals please.

UPDATED to fix typo in generated code

@machineghost

To be perfectly honest, Prettier has no interest in how people write their code. Users can very well write everything in a single line and Prettier won't say a single word about that, it'll simply format it :)

More seriously though, it's a misconception that the maintainers want to dictate style of everyone's code.. there are several examples of: things Prettier prints that isn't aligned on how we personally would write our code; options Prettier now accepts that almost none of us use and yet we coded an maintain them; and more examples of things we happily do, maintain or fix that don't use at all.. all this to say we have 0 interest in pushing "our" style..

Babel offers presets, that's great as they put every Babel-using developer on the same page. But Babel certainly doesn't try and stop anyone from adopting a module that's not in that preset, and I don't think "The Javascript Code Formatting Tool" should be any different from "The Javascript Transpiling Tool".

I can't understand this, Babel and Prettier are tools with completely different goals, I can't see how they are similar at all.

@dlombardi

From https://github.com/prettier/prettier/issues/2550#issuecomment-368275296:

What we are lacking is a heuristic to use to satisfy this problem without introducing style regressions.

We never said the current output is better but we just can't change it to simply always print multiline. We'd have this whole argument again with people asking to inline destructuring in some cases.

@ericanderson heuristic groundwork has my full support 👍

I want to think through a few more cases.

N=4, M=2, O=4

let foo;

// #1
foo = { bar };

// #2
foo = { bar, baz, bloop, blam };

// #3
foo = { 
  bar, 
  baz, 
  bloop, 
  blam,
  bong,
};

// #4
foo = {
  bar: { baz, bloop, blam, bong}
}

// #5
foo = [ baz, bloop, blam, bong ];

// #6 
foo = [
  bar, 
  baz, 
  bloop, 
  blam,
  bong,
];

// #7
foo = {
  foo: doSomething()
}

// #8
foo = { foo: doSomething } // reference, not call

// #9
foo = [ isBaz ? getBar() : getBloop() ];

// #10
foo = [ 
  {
    bar: [ isBaz ? getBar() : getBloop() ],
  }
];

// #11
foo = [ 
  {
    bar: [ baz, bloop, bam ],
  }
];  

```js
// #12
foo = [
{
bar: [
isBaz ? getBar() : getBloop(),
5,
],
}
];

@duailibe @j-f1 @azz @suchipi thoughts on the proposal? I might take a stab at it if there is support

It's pretty complicated, but it seems like a good heuristic. However, I think that:

  • N, M, and O should NOT be configurable
  • This should only apply to object destructuring, not object literals.

Actually, thinking about it some more, I think N, M, and O should be based more on print width and less on arbitrary numbers, otherwise we will introduce confusing style regressions...

How about:

An object is "simple" if it only contains shorthand properties, and is "complex" otherwise.
An array is "simple" if it contains no computed properties.

Rules:
If an object is simple, it's single-line if that fits under the print width
Otherwise, it's multi-line

Interesting. For completeness I will paste out all my examples again but for your proposed rules @suchipi

Im going to tweak it to say that an array is "simple" if it contains no computed properties OR only 1 computed property

let foo;

// #1
foo = { bar };

// #2
foo = { bar, baz, bloop, blam };

// #3
foo = { bar, baz, bloop, blam, bong };

// #4
foo = {
  bar: { baz, bloop, blam, bong }
}

// #5
foo = [ baz, bloop, blam, bong ];

// #6 
foo = [ bar, baz, bloop, blam, bong ];

// #7
foo = {
  foo: doSomething()
}

// #8
foo = {
  foo: doSomething
} // reference, not call

// #9
foo = [ isBaz ? getBar() : getBloop() ];

// #10
foo = [ 
  {
    bar: [ isBaz ? getBar() : getBloop() ],
  }
];

// #11
foo = [ 
  {
    bar: [ baz, bloop, bam ],
  }
];  

// #12
foo = [
  {
    bar: [ 
      isBaz ? getBar() : getBloop(),
      5,
    ],
  }
];

I tweaked your rules because I wouldnt want 9 to render as:

foo = [
  isBaz ? getBar() : getBloop()
];

Although I guess I don't mind that either. So your original rules seem reasonable.

Taking the OP's larger example we get

const {
  name: { first, last },
  organisation: {
    address: {
      street: orgStreetAddress,
      postcode: orgPostcode,
    },
  },
} = user;

I'm a bit confused; all your examples are object and array literals, not destructuring patterns. I thought this issue was about destructuring patterns

It’s both. The complaint includes that they behave differently.

Yeah people complain that they behave differently (as in object literals you can force multiline whereas when destructuring you can't) but the solution is not to make them behave the same, but figure out heuristics of when to break the destructuring.

I agree with @suchipi that we should not change object (or array) literals at all. Only destructuring and I'd start with object destructuring first..

That’s fair. I think @suchipi s rules work nicely for the object restructuring case though. Still happy to address a fix that is much much smaller in scope

If this issue gets resolved and PR merged in for treating destructured objects the same as object literals, I will get on the Prettier bandwagon again. As it is, now, it makes my code look like mush. :/

When trying to implement prettier in our project we fell into this issue and we think this is a big loss for us.

It was mentioned before but there is a big loss in:

  • readability for objects in most of the cases
  • worst change tracking in GitHub since when you want to add a prop, you have to modify the entire line with props that you are not modifying instead of just adding what you need

@azz, is this proposal something that is still being considered?

@nescalante If the issue is open, it's still being considered

Has there been progress on this?

I just entered @Brian-Gaffney's original example in the playground (with default settings) and got the desired result (apologies for the lengthy post):

Prettier 1.11.1
Playground link

--range-end 0
--range-start 0

Input:

// Object with multiple lines uncollapsed by Prettier
const user = {
  name: {
    first: "Zoid",
    last: "Berg",
  },
};

// Similar destructuring assignment is collapsed by Prettier
const {
  name: {
    first,
    last,
  },
} = user;

Output:

// Object with multiple lines uncollapsed by Prettier
const user = {
  name: {
    first: "Zoid",
    last: "Berg",
  },
};

// Similar destructuring assignment is collapsed by Prettier
const {
  name: {
    first,
    last,
  },
} = user;

Followed by his extended example which appears unchanged:

Prettier 1.11.1
Playground link

--range-end 0
--range-start 0

Input:

const {
  name: {
    first,
    last,
  },
  organisation: {
    address: {
      street: orgStreetAddress,
      postcode: orgPostcode,
    },
  },
} = user;

Output:

const {
  name: { first, last },
  organisation: { address: { street: orgStreetAddress, postcode: orgPostcode } }
} = user;

But building on that example to include the object being destructured seems to produce the desired result 😕 (edit: although dangling commas are missing from object assignment) :

Prettier 1.11.1
Playground link

--range-end 0
--range-start 0

Input:

const user = {
  name: {
    first: 'Zoid',
    last: 'Berg',
  },
  organisation: {
    address: {
      street: '1640 Riverside Drive',
      postcode: '23242',
    },
  },
};

const {
  name: {
    first,
    last,
  },
  organisation: {
    address: {
      street: orgStreetAddress,
      postcode: orgPostcode,
    },
  },
} = user;

Output:

const user = {
  name: {
    first: "Zoid",
    last: "Berg"
  },
  organisation: {
    address: {
      street: "1640 Riverside Drive",
      postcode: "23242"
    }
  }
};

const {
  name: {
    first,
    last,
  },
  organisation: {
    address: {
      street: orgStreetAddress,
      postcode: orgPostcode,
    },
  },
} = user;

Is there a reason for the inconsistency between the two extended examples? Is there cause for celebration?

@narkowicz sorry for the confusing, it's caused by a bug in our playground (#4273), as a temporary workaround you'll have to set --range-end to something like 999 to include all the content.

I've just merged a PR (#4267) that addresses some of the cases reported here. You can try it out on the PR's playground.

I just found this issue when looking for solutions to my current code formatting conflict between eslint and prettier and I'd like to know if there's anything to resolve it.

What I want to be able to have is:

const {deleteCommandMessages, roundNumber, stopTyping, startTyping } = require('../../util.js');

but what prettier does for me right now is:

  {
    deleteCommandMessages,
    roundNumber,
    stopTyping,
    startTyping
  } = require('../../util.js');

Now this entire thread is kind of confusing to me because from the title I would presume it is already possible to NOT have multi-line formatting, but then again I see no option about this in the docs at all. So, can I tell Prettier to not put each destructured property on a new line?

Edit: Further analysis made me realize this only happens starting 4 properties, but I want it to occur never.

Here is my current eslintrc file

@Favna We recommend disabling all ESLint rules that conflict with Prettier, for example using eslint-config-prettier

Sadness. So that basically means I cannot use Prettier because there is no option to disable that new line formatting.

@Favna why can’t you disable the ESLint rule?

@Favna, from what I understand, Prettier is an _opinionated_ formatter. So, you can use it, but it's definitely going to format your destructured object with that many properties to a new line, and I don't think they have plans to add additional configuration to change that.

@j-f1 It's not an eslint rule that causes the issue. Eslint doesn't care if you put destructured objects across 1 line. The closest thing to it is object-property-newline but even that does not affect this newline behavior.

I'd say @mssngr summed it up nicely then. Since I cannot conform myself to the opinionated formatting that Prettier uses it sadly is not for me.

It’s unfortunate that prettier is actually two things conflated into one: a performant, AST-based code formatter; and an opinionated style guide. The former is unique and valuable, but the latter is just another in a cluttered category.

This issue page is gigantic!

This is still being discussed right? Hasn't been implemented? A team member asked for multiline destructuring and I use Prettier and this is the first google hit. So if it hasn't been implemented then I'll need to work around it.

@jcollum We're now breaking when there's nested destructuring.. see the blog post of 1.12.

We're not sure this does address all the cases discussed here, so the issue is still open.

This issue page is gigantic!

They tend to be on popular projects.

Weirdest thing ever:

Before:

const { a } = {};

After:

const {
    a
} = {};

Seriously fix that or at least add an option to disable formatting of destructuring.

@finalclass I can’t reproduce that on the playground:

Prettier 1.12.1
Playground link

Input:

const { a } = {};

Output:

const { a } = {};

Can you create a reproducible example of your issue there and post it as a new issue?

So there must be something wrong with prettier vscode plugin:

ezgif-1-d9de541b60

@finalclass could you raise this over at https://github.com/prettier/prettier-vscode/issues? While it is similar to this issue, the root cause is mostly likely very different.

@azz Sure no problem!

Regardless of the specific rules, object destructuring should be treated exactly the same as object assignment. I think the examples below show how odd it is to treat them any differently:

Before:

const example = {
  hello: "world",
  foo: "bar",
};

const {
  hello: world,
  foo: bar,
} = example;

const shorthandExample = {
  definedEarlier,
  anotherOne,
};

const {
  helloWorld,
  fooBar,
} = simpleDestructureExample;

After: 😢

const example = {
  hello: "world",
  foo: "bar"
};

const { hello: world, foo: bar } = example;

const shorthandExample = {
  definedEarlier,
  anotherOne
};

const { helloWorld, fooBar } = simpleDestructureExample;

Right now, object assignment maintain multiline if that's how the code was written. But if a new rule is introduced in the future (e.g., to keep shorthand object assignment on a single line) then the same should apply to the destructuring (in this example, simple destructuring without aliases or default values).

Does anyone know of a different formatter that has this option?

Prettier 1.15.1
Playground link

Input:

const {
  foo,
  bar,
  baz,
  foofoo,
  barbar,
  bazbaz,
  foofoofoo,
} = destructureShort;

const { 
  foo,
  bar,
  baz,
  foofoo,
  barbar,
  bazbaz,
  foofoofoo,
  barbarbar
} = destructureLong;

const objectShort = {
  foo,
  bar,
  baz,
  foofoo,
  barbar,
  bazbaz,
  foofoofoo
};

const objectLong = {
  foo,
  bar,
  baz,
  foofoo,
  barbar,
  bazbaz,
  foofoofoo,
  barbarbar
}

Output:

const { foo, bar, baz, foofoo, barbar, bazbaz, foofoofoo } = destructureShort;

const {
  foo,
  bar,
  baz,
  foofoo,
  barbar,
  bazbaz,
  foofoofoo,
  barbarbar
} = destructureLong;

const objectShort = {
  foo,
  bar,
  baz,
  foofoo,
  barbar,
  bazbaz,
  foofoofoo
};

const objectLong = {
  foo,
  bar,
  baz,
  foofoo,
  barbar,
  bazbaz,
  foofoofoo,
  barbarbar
};

Just encountered this issue in the code base I'm working on. Showing my example here to demonstrate the fact that the short destructuring example is inconsistent with the other three examples of object definition/destructuring code.

Now that it’s less of a hassle to collapse expanded objects, perhaps this would be OK to do.

cc @prettier/core on my above comment ⬆️

I’d rather try to find cases where we can avoid respecting \n in objects rather than trying to add more of it.

> The output seems perfectly readable to me.

  • Because is sooooo readable use 6 lines to express what you just need 1 line.
    For me, prettier should brake only code that exceeds line max length limit

For me, prettier should brake only code that exceeds line max length limit

@leojnxs Sure but that's not how Prettier works.

Please be more respectful

@leojnxs Sure but that's not how Prettier works.

Please be more respectful

@duailibe I think everybody here knows how prettier works.
But we don't agree with everything and we are here trying to suggesting things to make it even more better.

Sorry if I was not respectful, Sure but that's not how Prettier works. is not the best example of respectful answer, too.

Sry for hurt your feelings, get better. Love Prettier. Love you. Have a nice day!

It seems like different people have different preferences for things. Crazy, I know... Maybe a highly opinionated formatter that only offers the bare minimum configuration options isn't the right tool for the vast majority of users.

It's certainly easier to not offer this option and the project maintainers can do what they want, but if enough people don't like it they'll jump ship as soon as something better comes along that offers more freedom.

Personally, I'd like more options for how my code wraps.

but if enough people don't like it they'll jump ship as soon as something better comes along that offers more freedom.

That would be great! If someone makes a tool that fits peoples’ workflows better than Prettier, we’d be happy to see it grow. See https://github.com/prettier/prettier/issues/3888#issuecomment-445063240 for a great explanation of why we don’t want to add options.

The issue is that Prettier tries to guess the more readable formatting based on a dumb heuristic: if it's even one character past a threshold, then wrap, otherwise, inline. This leads to Prettier sometimes guessing wrong, and also to diff churn. I think the best solution would be for Prettier to never inline, so that there's no diff churn, and for code editors and other tools displaying code to inline the code only for reading, and wrap the inline parts if they're selected for editing or feature a diff.

A more grounded solution is just to trade some consistency for more readability and less diff churn (like with object literals), or at least to make the wrapping heuristic more flexible and have an in-between range where it can be either inline or wrapped.

The explanation is big on consistency and a glaring inconsistency is being pointed out here. So your explanation is actually proving the point of the issue being raised. In light of that explanation, a change is clearly needed to address this issue.

Besides that, the whole explanation rests on 2 completely subjective notions:

  1. What is "readable".
  2. That spending time think about how to format your code somehow "doesn't matter".

I'd call that a terrible explanation.

I think the best solution would be for Prettier to never inline

That’s already possible — just set printWidth to 1.

That’s already possible — just set printWidth to 1.

Sure, but the point is that I'd still like my editor and code review tool to inline it for reading, just not when it's being edited or has changes.

That sounds like a separate project, probably in the form of an editor plugin.

@j-f1 Is there any hope to have prettier keep multi-line formatting for destructuring assignment in the near future?

I don’t think so.

I'm fine without it - not because I think that the lack of any options surrounding multi-line formatting is the right decision, but because it's entertaining to watch my code switch from multi-line to single line and back whenever I change a couple of characters and hit save... Spending the requisite time to find my place again always reminds me that "I'm doing this for the team" and "Maybe someday something better will come along or things will change". Things are always changing, you know? Keeps life interesting.

Anyway, Prettier is certainly opinionated, but opinions can be changed. Perhaps if you want a better response, someone could make a funny YouTube video with Benny Hill theme music playing as you do a few find and replaces that cause the formatter to go crazy every time you save. Then post it to places like Hacker News, Reddit, Slashdot and elsewhere. It's not for me, but it could be fun.

This discussion is so lengthy that it's hard to follow the current thinking here. I'm personally just wondering why destructured assignments don't follow the same rules as multi-line objects, as described in the docs. That doesn't imply a need for any new options.

The disconnect is odd to me, but I'm sure there's a reason the team doesn't want to do it. I see it doing it for "nested" objects, which is nice for sure.

Single Line to Multi-line Playground link

I'm personally just wondering why destructured assignments don't follow the same rules as multi-line objects, as described in the docs.

As mentioned in the docs, object literals are used for a lot of different things in JavaScript and we haven't been able to find a good rule for all those cases, so we went with the "solution" of letting the user decide. But kind of the whole point of Prettier is _not_ letting the user decide on formatting things. Desctructuring, on the other hand, is used for destructuring only and using Prettier's normal linebreak rules works better (though not perfectly) there. Quoting the second comment of this thread:

the special-casing of objects is more of a failing in prettier than a feature, I don't think it makes sense to spread that to more cases.

The issue is that Prettier tries to guess the more readable formatting based on a dumb heuristic: if it's even one character past a threshold, then wrap, otherwise, inline.

@slikts is correct, that is a dumb heuristic. However, it's the best heuristic we could find and make it work. And well, turns out it works fine for the majority of cases.

We'd love to see more tools that use other heuristics.

Any solution that conditionally inlines would cause diff churn, and inline code will always be less convenient to edit; my ideal solution would be to only have virtual inlining in the editor while the actual formatting that's saved on disk would have no inlining, so that there's no diff churn. The virtual inlining would be based on line length like now, and also on cursor position, so moving the cursor to a line would expand it to multiple lines.

@slikts I'm looking forward to see other solutions in this problem space.

Any progress as of late? I think the diff-friendliness is a strong argument for this feature.

I think the best solution would be for Prettier to never inline

That’s already possible — just set printWidth to 1.

That forces ALL assignments to break into a new line. What we need is printWidth: false

That’s not possible @gkatsanos — Prettier’s core algorithm depends on a defined printWidth.

Here is probably the least effort way of forcing prettier to do what you want:

const {
  //
  x,
  y,
  z,
} = yadaYadaYada();

Ironically, my code is now ugly.

Can't we just have this one option because it makes so much sense when compared to object literals? If you search around, there are a lot of people asking for it - not just here.

If I wrote the function like this, prettier leaves it alone:

Is it a proposal or a description of current behavior? If the latter, I can't reproduce it. Could you add links to the playground?

Haha - you know what? It was leaving it alone because it couldn't parse it. My mistake - I wasn't even paying attention to what I was typing.

(If you can't see the history of my comment - I basically forgot to type function names for my functions lol.)

Zero movement on this?

I have tons of code like this now and I'd really like to clean it up without having to fork this entire project:

function myFunction({
  //
  xxx,
  yyy,
  zzz,
}) {
 ...
}

JSON.stringify(object, undefuned, 2) do as i want. Just do like this, please. One-line - hell.

Was this page helpful?
0 / 5 - 0 ratings