Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Desired Prettier formatting of (nested) ternaries
// remain on one line if possible:
const short = isLoud() ? makeNoise() : silent();
// next, put everything after the =
const lessShort =
isLoudReallyLoud() ? makeNoiseReallyLoudly.omgSoLoud() : silent();
// next, push the alternate to a new line at the same level of indentation:
const allNextLine =
isLoudReallyReallyReallyReallyLoud() ? makeNoiseReallyLoudly.omgSoLoud()
: silent();
// next, indent the consequent, too:
const andIndented =
isLoudReallyReallyReallyReallyLoud() ?
makeNoiseReallyReallyReallyReallyReallyLoudly.omgSoLoud()
: silent();
// if chained, always break and put after the =
const chainedShort =
isCat() ? meow()
: isDog() ? bark()
: silent();
// when a consequent breaks in a chain:
const chainedWithLongConsequent =
isCat() ?
someReallyLargeExpression
.thatWouldCauseALineBreak()
.willCauseAnIndentButNotParens()
: isDog() ? bark()
: silent();
// nested ternary in consequent:
const chainedWithTernaryConsequent =
isCat() ?
aNestedCondition ? theResult()
: theAlternate()
: isDog() ? bark()
: silent();
// consequent and terminal alternate break:
const consequentAndTerminalAlternateBreak =
isCat() ?
someReallyLargeExpression
.thatWouldCauseALineBreak()
.willCauseAnIndentButNotParens()
: isDog() ? bark()
: (
someReallyLargeExpression
.thatWouldCauseALineBreak()
.willCauseAnIndentButNotParens()
);
// multiline conditions and consequents/alternates:
const multilineConditionsConsequentsAndAlternates =
(
isAnAdorableKittyCat() &&
(someReallyLongCondition || moreInThisLongCondition)
) ?
someReallyLargeExpression
.thatWouldCauseALineBreak()
.willCauseAnIndentButNotParens()
: (
isNotAnAdorableKittyCat() &&
(someReallyLongCondition || moreInThisLongCondition)
) ?
bark()
: shortCondition() ? shortConsequent()
: (
someReallyLargeExpression
.thatWouldCauseALineBreak()
.willCauseAnIndentButNotParens()
);
// illustrating case of mostly short conditionals
const mostlyShort =
x === 1 ? "one"
: x === 2 ? "two"
: x === 3 ? "three"
: (
x === 5 &&
y === 7 &&
someOtherThing.thatIsSoLong.thatItBreaksTheTestCondition()
) ?
"four"
: x === 6 ? "six"
: "idk";
// long conditional, short consequent/alternate, not chained - don't indent after ?
const longConditional =
(
bifornCringerMoshedPerplexSawder === 2 / askTrovenaBeenaDependsRowans &&
glimseGlyphsHazardNoopsTieTie >=
averredBathersBoxroomBuggyNurl().anodyneCondosMalateOverateRetinol()
) ? "foo"
: "bar";
// long conditional, short consequent/alternate, chained
// (break on short consequents iff in chained ternary and its conditional broke)
const longConditionalChained =
(
bifornCringerMoshedPerplexSawder === 2 / askTrovenaBeenaDependsRowans &&
glimseGlyphsHazardNoopsTieTie >=
averredBathersBoxroomBuggyNurl().anodyneCondosMalateOverateRetinol()
) ?
"foo"
: anotherCondition ? "bar"
: "baz";
// As a function parameter, don't add an extra indent:
definition.encode(
typeof row[field] !== "undefined" ? row[field]
: typeof definition.default !== "undefined" ? definition.default
: null,
typeof row[field] === "undefined" ?
typeof definition.default === "undefined" ? null
: definition.default
: row[field]
);
// In a return, nest in parens if breaking:
const inReturn = () => {
if (short) {
return foo ? 1 : 2
}
return (
typeof row[aVeryLongFieldName] !== "undefined" ? row[aVeryLongFieldName]
: null
);
};
// Remove current JSX Mode, and replace it with this algorithm:
// When a ternary's parent is a JSXExpressionContainer which is not in a JSXAttribute,
// and the consequent is not `null`,
// force the consequent to break,
// and force the terminal alternate to break if it is a JSXElement;
// when the consequent is `null`,
// do not add a line before or after it.
const someJSX = (
<div>
Typical jsx case:
{showTheThing || pleaseShowTheThing ?
<Foo attribute="such and such stuff here" />
: (
<Bar short />
)}
Nested, and with a non-jsx consequent:
{component ?
React.createElement(component, props)
: render ?
<div>{render(props)}</div>
: (
<div>Nothing is here</div>
)}
When the terminal alternate isn't a JSXElement, we don't force it to break:
{showTheJSXElement ?
<div>the stuff</div>
: renderOtherStuff()}
When the consequent is `null`, don't put a linebreak before or after it:
{!thing ? null : (
<TheThing thing={thing} />
)}
</div>
);
ternaryWithJSXElements.hasNoSpecialCasing =
component ? <div>{React.createElement(component, props)}</div>
: render ? <div>{render(props)}</div>
: <div>Nothing is here</div>;
jsxExpressionContainer.inJSXAttribute.hasNoSpecialCasing = (
<Foo
withJSX={
isABeautifulRichDeepRedColor.andNotGreen ? <RedColorThing />
: <GreenColorThing />
}
/>
);
// TypeScript has the same behavior, including a line break after =, but no parens around "conditional":
type KnownKeys<T> =
{
[K in keyof T]: string extends K ? never
: number extends K ? never
: K;
} extends { [_ in keyof T]: infer U } ?
{} extends U ? never
: U
: never;
type KnownKeysWithLongExtends<T> =
{
[K in keyof T]: string extends K ? never
: number extends K ? never
: K;
} extends {
[_ in keyof T]: SomeReallyLongThingThatBreaksTheLine<infer U>
} ? U
: never;
// TypeScript examples:
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 ?
SomeReallyLongThingThatBreaksTheLine<U>
: T extends Promise<infer U> ? U
: T;
/**
* ========================================================================
* EXAMPLES
* mostly taken from https://github.com/prettier/prettier/issues/9561
* ========================================================================
*/
const message =
i % 3 === 0 && i % 5 === 0 ? "fizzbuzz"
: i % 3 === 0 ? "fizz"
: i % 5 === 0 ? "buzz"
: String(i);
const paymentMessage =
state == "success" ? "Payment completed successfully"
: state == "processing" ? "Payment processing"
: state == "invalid_cvc" ? "There was an issue with your CVC number"
: state == "invalid_expiry" ? "Expiry must be sometime in the past."
: "There was an issue with the payment. Please contact support.";
const paymentMessage =
state == "success" ? "Payment completed successfully"
: state == "processing" ? "Payment processing"
: state == "invalid_cvc" ?
"There was an issue with your CVC number, and you need to take a prompt action on it."
: state == "invalid_expiry" ? "Expiry must be sometime in the past."
: "There was an issue with the payment. Please contact support.";
const result =
definition.encode ?
definition.encode(
typeof row[field] !== "undefined" ? row[field]
: typeof definition.default !== "undefined" ? definition.default
: null
)
: typeof row[field] !== "undefined" ? row[field]
: typeof definition.default !== "undefined" ? definition.default
: null
// (the following is semantically equivalent to the above, but written in a more-confusing style – it'd be hard to grok no matter the formatting)
const result =
definition.encode ?
definition.encode(
typeof row[field] === "undefined" ?
typeof definition.default === "undefined" ? null
: definition.default
: row[field]
)
: typeof row[field] === "undefined" ?
typeof definition.default === "undefined" ? null
: definition.default
: row[field];
// TypeScript examples:
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;
// JSX Examples:
const typicalLongConsequentWithNullAlternate = (
<div>
{children && !isEmptyChildren(children) ?
<FooComponent
className="a bunch of css classes might go here, wow so many"
foo={foo}
bar={includeBar ? bar : null}
/>
: null}
</div>
);
const reactRouterExampleJSX = (
<div>
{children && !isEmptyChildren(children) ?
children
: props.match ?
component ? React.createElement(component, props)
: render ? render(props)
: null
: null}
</div>
);
const reactRouterExampleNonJSX =
children && !isEmptyChildren(children) ? children
: props.match ?
component ? React.createElement(component, props)
: render ? render(props)
: null
: null;
inJSXExpressionContainer.withLongConditionals.example = (
<div>
{(
isACat() &&
(someReallyLongCondition ||
moreInThisLongCondition)
) ?
someReallyLargeExpression
.toMakeMeowNoise()
.willCauseParens()
: (
someReallyLongCondition ||
moreInThisLongCondition
) ?
bark()
: (
someReallyLargeExpression
.toMakeMeowNoise()
.willCauseParens()
)}
</div>
);
inJSXExpressionContainer.withLoops.orBooleans.example = (
<div>
{items ?
items.map((item) => (
item.display ? <Item item={item} attr="breaks ternary but not consequent" />
: <Blank />
))
: null}
{showTheStuff && (
foo ? <Thing thing={foo} />
: <OtherThing />
)}
</div>
);
@rattrayalex

This comment has been minimized.

Copy link
Owner Author

@rattrayalex rattrayalex commented May 14, 2021

Benefits:

  1. Similar behavior to JSX when the consequent is large, just without the parens
  2. Always has ? right after a conditional, which matches natural language
  3. Always has : at the beginning of an else
  4. Nice and concise when it can be, which will look especially nice w/ TS.
  5. Definitely looks like ternaries, not too wild/crazy/different.
  6. Close to the "case/table style" that was popular with the community, a lot of people really like this idea (I just didnt think we could make it work)
  7. Works much better with useTabs, no 2-space indents.
@rattrayalex

This comment has been minimized.

Copy link
Owner Author

@rattrayalex rattrayalex commented May 16, 2021

The stream-of-consciousness reasoning for why to adopt this new JSX formatting is here: https://gist.github.com/rattrayalex/6c32515457d5c1b3667050c895ec4510#file-what-if-we-removed-jsx-mode-jsx

@rattrayalex

This comment has been minimized.

Copy link
Owner Author

@rattrayalex rattrayalex commented May 23, 2021

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment