Prettier
Prettier 更新了3.1版本,对三元组的格式化做了更新
Prettier 3.1 Released — The popular opinionated code formatter has a new release including support for the new control flow syntax in Angular 17 plus a new, experimental formatting option for ternary expressions (as in x ? y : z) explained in more depth in “A curious case of the ternaries.”
opinionated: 这个没有直接对应的中文意思,类似于褒义版本的刚愎自用,在 Prettier 的语境下,就是如果不按它那么写就不行,就会报错画红波浪线
control flow 控制流
syntax 语法
experimental 实验性的
A curious case of the ternaries
https://prettier.io/blog/2023/11/13/curious-ternaries.html
Formatting nested ternaries 格式化 嵌套的 三元组
Ternary formatting has always been a challenge, and we’re finally addressing it in v3.1.0 with the introduction of a novel formatting style.
addressing 发表
novel 新的
Read on for our journey and the motivation behind this change, along with early developer feedback and an overview of the “curious ternaries” style.
Read on 继续阅读 Please read on for more information.
motivation 动机
introduction
Formatting nested ternaries nicely in a wide variety of scenarios is a surprisingly tricky challenge.
scenarios 场景
tricky challenge 棘手的挑战
Developers have long found them so confusing to read that they end up just refactoring their code to an ugly series of if-else
statements, often with a let
declaration, an iife, or a separate function entirely.
declaration 声明
iife 立即调用表达式 immediately invoked function expression
According to beta testers, the new formatting style we’ve developed can take some getting used to, but ultimately allows ternaries to be practically used as a concise form of if-else-expressions in modern codebases.
getting used to 开始适应,take some getting used to 是说需要一段时间适应
ultimately 同 finally,还有一个意思是表示强调和重要
concise 简明的
Historical background
Prettier’s original, naïve approach – just add indentation to each level of a nested ternary – worked fine in simple cases, but obviously doesn’t scale to long chains of nested ternaries and had other problems.
naïve 同 naive
indentation 缩进
So in 2018, we replaced that with flat ternaries, which seemed like a good idea at the time, but was not received well – the issue asking it to be reverted had well over 500 upvotes.
revert 还原
While we did ultimately revert back to indented ternaries, we wanted to find a better way.
Over the last few years, we explored and experimented with many, many possible solutions which would be as readable as indented ternaries in common cases, but also scale to work well in a wider variety of situations.
Challenging criteria
criteria 标准
Ideally, we’d find one scheme that would meet our criteria:
Ideally 理想情况下
scheme 方案
- In all cases, it should be easy to follow “what’s the if”, “what’s the then”, and “what’s the else” – and what they map to.
map to 映射到
- The code should fluidly flow from a single ternary, to a chain of 2, to a long chain of simple cases, to something more complex with a few nested conditions. (Most alternatives we explored failed this test).
fluidly 流畅地
这个我没大看懂,是优先级的意思嘛
- The syntax in JSX, TypeScript conditional expressions (which cannot be expressed with if), and normal JS should all look and feel the same.
比方说 TypeScript 里面有
interface Animal {
live(): void;
}
interface Dog extends Animal {
woof(): void;
}
type Example1 = Dog extends Animal ? number : string;
// type Example1 = number
type Example2 = RegExp extends Animal ? number : string;
// type Example2 = string
这里的三元表达式就不能直接用 if 表达式代替
jsx中
render() {
const isLoggedIn = this.state.isLoggedIn;
return (
<div>
{isLoggedIn
? <LogoutButton onClick={this.handleLogoutClick} />
: <LoginButton onClick={this.handleLoginClick} />
}
</div>
);
}
也不能直接用 if 表达式代替
- It should scale to nested ternary chains of arbitrary length (imagine a TypeScript conditional type with dozens of alternative cases).
scale to 从一种情况扩展到另一种情况
arbitrary 任意的
这里是指 TS 的情况
type TypeName<T> = T extends string
? "string"
: T extends number
? "number"
: T extends boolean
? "boolean"
: T extends undefined
? "undefined"
: T extends Function
? "function"
: "object";
这样缩进就没有必要了,太冗长
Indented ternaries clearly failed (4), arguably (1), and even (3) – we have almost always printed JSX ternaries in a flat-but-readable format that unfortunately felt unnatural outside of JSX.
我看了他们的链接,JSX 风格就是有好多的括号和换行,看起来就像用小括号括起来的 if else 一样,类似于以下这种:
const StorybookLoader = ({ match }) =>
match.params.storyId === "button" ? (
<ButtonStorybook />
) : match.params.storyId === "color" ? (
<ColorBook />
) : match.params.storyId === "typography" ? (
<TypographyBook />
) : match.params.storyId === "loading" ? (
<LoaderStorybook />
) : match.params.storyId === "deal-list" ? (
<DealListStory />
) : (
<Message>
<Title>{"Missing story book"}</Title>
<Content>
<BackButton />
</Content>
</Message>
);
但是呢,非jsx的场景下确实很繁琐
// JSX 型格式
const message =
i % 3 === 0 && i % 5 === 0 ? (
"fizzbuzz"
) : i % 3 === 0 ? (
"fizz"
) : i % 5 === 0 ? (
"buzz"
) : (
String(i)
);
Many people in the community were excited about a “case-style”, drawing inspiration from the match syntax from languages like Rust or OCaml, but it did not meet (2) and other goals.
case-style 看起来很优雅,但是体现不出来层次的关系
// case-style 格式
const message =
i % 3 === 0 && i % 5 === 0 ? "fizzbuzz" :
i % 3 === 0 ? "fizz" :
i % 5 === 0 ? "buzz" :
String(i);
A surprising solution
The good news is that we found a formatting algorithm that met our criteria. The bad news is that it’s novel, and thus unfamiliar to most developers.
novel 新
In beta testing this feature, we found developers were quite skeptical when they first saw it:
skeptical 怀疑的
But then, after using it for a bit, they didn’t want to go back:
Another developer had this to say:
My first hour with the rule on, it felt a little odd. But by hour two, I’d used it a few times to solve problems that otherwise would have been ugly refactors to if statements. I’m not going back.
I used to hate nested ternaries, but I also hate restructuring a nice line of code into if-else statements. The new rule adds an understandable, linear if-else, if-else expression to the language and is much nicer than multiple ternaries as nested branches.
So we felt we had a winning formula, but we knew it could be a jarring introduction to the community.
winning formula 这是一个俗语,指有效的方式,不一定是字面上的致胜法
jarring 令人震惊
As a result, we decided to put this new formatting behind a temporary --experimental-ternaries option for a few months, and in the meantime go ahead and ship what the community has been clamoring for: indented ternaries.
ship 本意是用船送,这里同 send, transport
clamoring for 吵着要,热切要求的感觉
Styling Overview
So what does this new style look like, anyway?
Here’s a quick, contrived example to show the thinking behind “curious” ternaries:
contrived 直译是人造的,在这应该不是这个含义,更像是它动词转化过来的,表示好
contrive: to think of or make something, for example a plan or a machine, in a clever way
const animalName =
pet.canBark() ?
pet.isScary() ?
'wolf'
: 'dog'
: pet.canMeow() ?
'cat'
: 'probably a bunny';
- Every line that ends with a question mark is an “if”.
- If you see foo ? it’s like asking a question about foo – “if foo? then, …”.
- Every line that starts with a : is an “else”.
- If you see : foo that means, “else, foo”.
- If you see : foo ? that means “else, if foo?”.
- Every line without : or ? is a “then”.
- If you just see foo, that means, “then foo”.
And here’s the code rewritten to demonstrate “case-style” ternaries:
demonstrate 演示
const animalName =
pet.isScary() ? 'wolf'
: pet.canBark() ? 'dog'
: pet.canMeow() ? 'cat'
: 'probably a bunny';
You can see this is a nice concise way to get something approaching match
-style syntax in JavaScript, with just the humble ternary operator (albeit missing several features).
humble 简陋,简单
albeit 甚至
Our new formatting is a fluid blend of “curious” ternaries (where the question mark is always at the end of the line), and “case-style” ternaries, where the question mark is in the middle of the line.
blend 混合
curious 好奇的,因为是问号结尾,所以显得很好奇
For example:
const animalName =
pet.canSqueak() ? 'mouse'
: pet.canBark() ?
pet.isScary() ?
'wolf'
: 'dog'
: pet.canMeow() ? 'cat'
: pet.canSqueak() ? 'mouse'
: 'probably a bunny';
Explicit Resource Management: Exploring JavaScript’s and TypeScript’s new feature
Explicit Resource Management 显式资源管理
One of my favorite new features of JavaScript and TypeScript is explicit resource management. It brings new syntax, using foobar = …, that enables RAII, reducing boilerplate when managing the lifecycle of various resources.