-
-
Notifications
You must be signed in to change notification settings - Fork 4.6k
Use JSX style for all nested ternaries #9552
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Another reason to prefer this style is tabs. "Use tabs for indentation, spaces for alignment" sounds good in theory but is confusing in practice. I avoid using tabs now because Prettier works best with spaces, specifically because of how it formats ternaries and union types (see #4198). Also I totally agree that if the main problem with the current formatting is not the indentation per se but the lack of a clear separation between conditions and results, then this style is a tidier way to achieve this separation than aggressive indentation. |
@rattrayalex , hey const message =
i % 3 === 0 && i % 5 === 0 ? (
"fizzbuzz"
) : i % 3 === 0 ? (
"fizz"
) : i % 5 === 0 ? (
"buzz"
) : (
String(i)
); it is much more noisy then ts conditional type style: const message =
i % 3 === 0 && i % 5 === 0 ? "fizzbuzz" :
i % 3 === 0 ? "fizz" :
i % 5 === 0 ? "buzz" :
String(i); Could "noise" be considered too? |
Original const pickBgColor = (colorName: string) =>
colorName === 'active'
? colorName === 'active'
? 'var(--blue2)'
: colorName === 'inactive'
: colorName === 'inactive'
? 'var(--blue1)'
: colorName === 'success' || colorName === 'abc'
? colorName === 'abc'
? 'var(--blue2)'
: 'var(--blue23)'
: 'var(--red4)';
const message = (i: number) =>
i % 3 === 0 && i % 5 === 0
? 'fizzbuzzfizzbuzzfizzbuzzfizzbuzz'
: i % 3 === 0
? i % 3 === 0
? 'ffizzbuzzfizzbuzzfizzbuzzfizzbuzzizz'
: i % 5 === 0
: i % 5 === 0
? 'fizzbuzzfizzbuzzfizzbuzzfizzbuzz'
: String(i); current PR const pickBgColor = (colorName: string) =>
colorName === "active" ? (
colorName === "active" ? (
"var(--blue2)"
) : (
colorName === "inactive"
)
) : colorName === "inactive" ? (
"var(--blue1)"
) : colorName === "success" || colorName === "abc" ? (
colorName === "abc" ? (
"var(--blue2)"
) : (
"var(--blue23)"
)
) : (
"var(--red4)"
);
const message = (i: number) =>
i % 3 === 0 && i % 5 === 0 ? (
"fizzbuzzfizzbuzzfizzbuzzfizzbuzz"
) : i % 3 === 0 ? (
i % 3 === 0 ? (
"ffizzbuzzfizzbuzzfizzbuzzfizzbuzzizz"
) : (
i % 5 === 0
)
) : i % 5 === 0 ? (
"fizzbuzzfizzbuzzfizzbuzzfizzbuzz"
) : (
String(i)
); The original is much better. The current PR is terrible. There is a lot of noise here, like in JSX. |
Having examples is helpful, thank you – especially when they are more real-world / have sensible code. I am now convinced that the "JSX style approach" is too simplistic to work well everywhere, so I will close this PR. I am however still optimistic (possibilistic?) that a solution may exist which truly improves the readability of nested/chained ternaries everywhere. @DScheglov , I am also starting to think that case-style might be a good starting point in general, eg: const result =
children && !isEmptyChildren(children) ? children :
props.match ? (
component ? React.createElement(component, props) :
render ? render(props) :
null
) :
null; I'm also interested in the if-else-style approach outlined here by @JAForbes, eg: const result =
children && !isEmptyChildren(children)
? children
: props.match
? (component
? React.createElement(component, props)
: render
? render(props)
: null)
: null; I've opened a separate issue to muse about the possibilities at greater length: #9561 |
@rattrayalex I draw the opposite conclusion from the provided examples (only one of which seems to be real-world code). In my opinion, the JSX style was easier to read in all cases. Longer, yes. More parens, yes. But not noisier than good ol’ if-else. I think we should reopen and at least give it a chance. |
@lydell fair, done! Though I gotta say, I really like the case-style when things are simple, and it naturally turns right into ~JSX-style when they're not: const message =
i % 3 === 0 && i % 5 === 0 ? "fizzbuzz" :
i % 3 === 0 ? "fizz" :
i % 5 === 0 ? "buzz" :
String(i);
const message =
i % 3 === 0 && i % 5 === 0 ? (
// pretend this is big and complex
"fizz" + "buzz"
) :
i % 3 === 0 ? (
// this too
"fizz"
) :
i % 5 === 0 ? (
// such a long line wow
"buzz"
) : (
// and this
String(i)
); The only difference being that const result =
children && !isEmptyChildren(children) ? children :
props.match ? (
component ? React.createElement(component, props) :
render ? render(props) :
null
) : null; |
@lydell btw, a big part of my motivation in opening #9561 is handling TS Conditional types nicely, which IMO look really bad with JSX-style: type TypeName<T> =
T extends string ? (
"string"
) : T extends number ? (
"number"
) : T extends boolean ? (
"boolean"
) : T extends undefined ? (
"undefined"
) : T extends Function ? (
"function"
) : (
"object"
);
type Unpacked<T> =
T extends (infer U)[] ? (
U
) : T extends (...args: any[]) => infer U ? (
SomeVeryLongNameOfSomeKind<U>
) : T extends Promise<infer U> ? (
U
) : (
T
); if members of the TS community disagree and find this the most readable option, I'd be quite happy to put this back on the table! |
@rattrayalex , I've posted real-world example in the Issue, see the comment: It is absolutely real. |
@rattrayalex WRT conditional types, see #7948 for some good code examples. |
Just musing aloud, I wonder about an "all-or-nothing" approach to chained ternaries, where if none of the ternaries break, you get this: const pickBgColor = (colorName) =>
colorName === 'active' ? 'var(--blue2)' :
colorName === 'inactive' ? 'var(--blue1)' :
colorName === 'success' ? 'var(--green4)' :
'var(--red4)'; but if any of them are too long for one line, they all become JSX-style... |
@rattrayalex I proposed a similar thing in #7948 for conditional types, but for normal (non-type) conditionals, it might be too diff-unfriendly to reformat an entire multiline expression upon changing only a small part of it. |
Ah, yes, that's a very good point. Really changes how I think about this to be honest. |
Hey, @rattrayalex , any option we choose (except keeping as is) will be diff-unfriendly, ... And one more option -- it is difficult, but possible. Prettier analyzes the source code and defines "current" style and after that checks style-consistency. |
Sorry, to be clear, the diff changes we are talking about are while editing code once this is released, not "at release of this change". Constant large diffs while editing code, in places unrelated to the line you're editing, happen sometimes with prettier but are great to avoid whenever possible. I appreciate the desire to be helpful, but adding new settings or having prettier "keep existing style" are antithetical to prettier's philosophy. As you can see from countless threads, prettier would rather make some (even many) people unhappy with how their code looks than adopt either approach. I am not a core maintainer; I don't make these rules, I just have to play by them. |
Adding a setting also means admitting that all the proposals are equally good/bad, so the whole problem is nothing more but bike shedding, in which case we can as well simply do nothing. I hope it's not the case though. |
I no longer think this is a good idea, should have closed this a while ago. I now believe that postfixed ternaries are the only scalable, readable, consistent alternative, proposed here as "Option B2": #9561 (comment) |
Fixes #5814
This is an alternative to #9559; please upvote this PR or #9559, or a proposal in #9561
I think this PR has better behavior since it scales better to long ternary formats, has an extremely clear visual difference between conditionals and consequents, and is easy to refactor. I've also had good experiences and heard good feedback on the JSX style when it is used in JSX.
Headline examples from the tests:
and a case I hope to add to the tests:
Note that this PR uses indents for TS Conditional Types, eg:
but we could easily back that change out.
I may seek to change TS Conditional Type formatting in a follow-on PR, and am considering this case-style format:
docs/
directory)changelog_unreleased/*/pr-XXXX.md
file followingchangelog_unreleased/TEMPLATE.md
.✨Try the playground for this PR✨