arrowParens β parentheses around a lone arrow parameter, and the edit cost of leaving them off
The Prettier Arrow Parens option
arrowParens decides whether a single-parameter arrow function keeps its parentheses. The default always writes (x) => x; avoid writes x => x. The argument for the default is not appearance but editability β almost every change you make to that parameter requires the parentheses back.
Include parentheses around a sole arrow function parameter.- Default
"always"- Type
choice- CLI flag
--arrow-parens- Allowed values
alwaysavoid
What the option does
It applies to exactly one case: an arrow function with a single parameter and no destructuring, default value or type annotation. Every other arrow already requires parentheses as a matter of syntax.
// always (default)
const double = (n) => n * 2;
// avoid
const double = n => n * 2;Why the default is always
Because the parentheses-free form is a local optimum that you keep having to undo. Consider the ways a lone parameter commonly changes:
- Adding a type annotation β
(n: number) => n * 2β requires parentheses. - Adding a default value β
(n = 1) => n * 2β requires parentheses. - Destructuring it β
({ id }) => idβ requires parentheses. - Adding a second parameter requires parentheses.
- Adding a leading comment or a decorator requires parentheses.
Each of those edits therefore touches the parameter and its punctuation, producing a slightly noisier diff than the change deserves. always pays two characters up front so that none of those edits ever costs more than the change itself.
The effect is strongest in TypeScript, where adding a type to a callback parameter is a routine edit rather than a rare one.
The case for avoid
avoid reads better in dense functional code. A chain of small transformations β items.map(x => x.id).filter(id => id != null) β is genuinely lighter without the parentheses, and if your codebase is full of one-line callbacks that argument carries real weight.
It is also the older convention: a lot of JavaScript written between roughly 2016 and 2019 omits them, so matching it can make a codebase feel consistent with its own history.
Common mistakes
- Expecting
avoidto strip parentheses from every arrow. It only affects the single-plain-parameter case; all others are syntactically required. - Choosing
avoidin a TypeScript codebase and then fighting it every time a parameter needs a type. - Leaving ESLintβs
arrow-parensrule enabled alongside Prettier, which produces a rule that can never be satisfied.
The default changed in Prettier 2.0
Prettier 1.x defaulted to avoid, so a great deal of code written in that era has bare single parameters. Prettier 2.0 changed the default to always, and the reasoning given was precisely the edit-cost argument above: the parentheses-free form has to be undone by most of the changes people actually make to a parameter.
If you are upgrading a project that has been pinned to Prettier 1 and you see every short arrow function change, this is the cause. Setting "arrowParens": "avoid" reproduces the old output exactly and lets you take the version bump without the reformat.
Why TypeScript pushes harder toward always
In JavaScript a callback parameter usually stays bare for its whole life. In TypeScript it frequently does not: an implicit any gets flagged, an inference fails, a signature changes, and a type annotation appears. Every one of those edits requires the parentheses.
With strict and noImplicitAny enabled β which is the default for new TypeScript projects β the compiler actively pushes you toward annotating parameters that are not contextually typed. avoid then means a steady trickle of diffs where the punctuation changes alongside the type.
// before
const byId = (u) => u.id;
// after adding a type
const byId = (u: User) => u.id;Use it in .prettierrc
Drop arrowParens into your Prettier config file:
{
"arrowParens": "always"
}Try it in the generatorWorked examples
The same code formatted with each value of arrowParens.
arrowParens: always
const user = {
name: "Ada",
"user-id": 7,
roles: ["admin", "editor"],
active: true,
};
const greet = (name) => `Hello ${name}`;
const label = user.active
? "active member of the team"
: "inactive member of the team";
export function summarize(items) {
return items
.filter((i) => i.active)
.map((i) => i.name)
.join(", ");
}arrowParens: avoid
const user = {
name: "Ada",
"user-id": 7,
roles: ["admin", "editor"],
active: true,
};
const greet = name => `Hello ${name}`;
const label = user.active
? "active member of the team"
: "inactive member of the team";
export function summarize(items) {
return items
.filter(i => i.active)
.map(i => i.name)
.join(", ");
}Allowed values
always- Always include parens. Example: `(x) => x`
avoid- Omit parens when possible. Example: `x => x`
Common questions
- Why does Prettier keep the parentheses even with avoid set?
- Because that particular arrow is not the single-plain-parameter case. Type annotations, default values, destructuring, rest parameters and multiple parameters all require parentheses as syntax.
- Which should a TypeScript project choose?
alwaysis the better fit. Annotating a callback parameter is a routine edit in TypeScript, and each one would otherwise have to add the parentheses back.
Other JavaScript options
Generated from Prettier 3.9.6.