Skip to content

noImplicitCoercions

biome.json
{
"linter": {
"rules": {
"complexity": {
"noImplicitCoercions": "error"
}
}
}
}

Encourage use of explicit type conversion functions over their shorthand counterparts.

JavaScript (due to its dynamic typing) automatically coerces values to and from different types when applying certain operators. As such, one can use these operators as a “shorthand” for coercing values between different types:

const answer = +"42"; // 42 (coerced to number)
const myStr = "" + answer; // "42" (coerced to string)
console.log(!!answer); // true (coerced to boolean)

While these “implicit coercions” can save space, there are several reasons one may prefer to avoid them:

  • Relying on these shortcuts can hurt readability, especially for newer developers less familiar with these patterns. Writing Boolean(value) or String(myNum) makes the type of the resulting value clear and explicit, as opposed to !!value or foo + "" (which may appear confusing at first glance).
  • TypeScript does not allow declaration merging for the built-in type coercion operators, unlike their more explicit function counterparts. For instance, +value cannot be overridden to return a more specific type under certain conditions (as opposed to Number(), whose method signatures can be customized to do exactly that).

This rule encourages the use of explicit type conversion functions like Boolean(), Number(), and String() in favor of implicit operator conversions.

A full list of constructs linted by this rule are as follows:

PatternTargetExample
Double negation1Boolean!!value
Unary plusNumber+value
Double unary negationNumber-(-value)
Subtraction with zero2Numbervalue - 0
Multiplication with one2Numbervalue * 1
Division with one2Numbervalue / 1
Concatenation with an empty string2Stringvalue + "", value +  “
Bitwise NOT with indexOf3Check against -1~arr.indexOf(value)
!!foo;
code-block.js:1:1 lint/complexity/noImplicitCoercions  FIXABLE  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Implicit type conversions are hard to read and understand.

> 1 │ !!foo;
^^^^^
2 │

Unsafe fix: Use Boolean() instead.

1 - !!foo;
1+ Boolean(foo);
2 2

+foo;
code-block.js:1:1 lint/complexity/noImplicitCoercions  FIXABLE  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Implicit type conversions are hard to read and understand.

> 1 │ +foo;
^^^^
2 │

Unsafe fix: Use Number() instead.

1 - +foo;
1+ Number(foo);
2 2

-(-foo);
code-block.js:1:1 lint/complexity/noImplicitCoercions  FIXABLE  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Implicit type conversions are hard to read and understand.

> 1 │ -(-foo);
^^^^^^^
2 │

Unsafe fix: Use Number() instead.

1 - -(-foo);
1+ Number(foo);
2 2

foo - 0;
code-block.js:1:1 lint/complexity/noImplicitCoercions  FIXABLE  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Implicit type conversions are hard to read and understand.

> 1 │ foo - 0;
^^^^^^^
2 │

Unsafe fix: Use Number() instead.

1 - foo·-·0;
1+ Number(foo);
2 2

foo * 1;
code-block.js:1:1 lint/complexity/noImplicitCoercions  FIXABLE  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Implicit type conversions are hard to read and understand.

> 1 │ foo * 1;
^^^^^^^
2 │

Unsafe fix: Use Number() instead.

1 - foo·*·1;
1+ Number(foo);
2 2

foo / 1;
code-block.js:1:1 lint/complexity/noImplicitCoercions  FIXABLE  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Implicit type conversions are hard to read and understand.

> 1 │ foo / 1;
^^^^^^^
2 │

Unsafe fix: Use Number() instead.

1 - foo·/·1;
1+ Number(foo);
2 2

foo + "";
code-block.js:1:1 lint/complexity/noImplicitCoercions  FIXABLE  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Implicit type conversions are hard to read and understand.

> 1 │ foo + "";
^^^^^^^^
2 │

Unsafe fix: Use String() instead.

1 - foo·+·"";
1+ String(foo);
2 2

'' + foo;
code-block.js:1:1 lint/complexity/noImplicitCoercions  FIXABLE  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Implicit type conversions are hard to read and understand.

> 1 │ ” + foo;
^^^^^^^^
2 │

Unsafe fix: Use String() instead.

1 - ''·+·foo;
1+ String(foo);
2 2

baz += ``;
code-block.js:1:1 lint/complexity/noImplicitCoercions  FIXABLE  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Implicit type conversions are hard to read and understand.

> 1 │ baz += “;
^^^^^^^^^
2 │

Unsafe fix: Use String() call instead.

1 - baz·+=·``;
1+ baz·=·String(baz);
2 2

~foo.indexOf(1);
code-block.js:1:1 lint/complexity/noImplicitCoercions  FIXABLE  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Using binary operations instead of comparisons is harder to read and understand.

> 1 │ ~foo.indexOf(1);
^^^^^^^^^^^^^^^
2 │

Unsafe fix: Compare with -1 instead.

1 - ~foo.indexOf(1);
1+ (foo.indexOf(1)·!==·-1);
2 2

Boolean(foo);
Number(foo);
String(foo);
foo.indexOf(1) !== -1;
`a${foo}`;
tag`${foo}`;

These are not flagged because they may have other effects on the produced value other than type coercion:

!foo;
~foo;
-foo;
+1234;
2 * foo;
foo + 'bar';
foo + 0; // has the potential to concatenate strings, unlike `foo - 0` which always produces a number

Whether to allow or disallow the use of double negation (!!value) for Boolean coercions.

Default: false (disallow)

Examples of correct code with allowDoubleNegation set to true:

biome.json
{
"linter": {
"rules": {
"complexity": {
"noImplicitCoercions": {
"options": {
"allowDoubleNegation": true
}
}
}
}
}
}
!!foo;

While one could make an argument to add options for each individual disallowed pattern, the other variants are significantly less common and tend to suffer even more from readability issues. As such, the choice was made (for the time being) to only allow toggling double negation given its relatively high frequency.

If you have a strong case to selectively allow one of the other patterns, open a feature request on GitHub and we can discuss it there!

  1. Unless the allowDoubleNegation option is set to true, in which case it is ignored.

  2. Including their assignment counterparts (+=, -=, *=, /=). 2 3 4

  3. Bitwise NOT produces the 2’s complement negation of a number, which is 0 for -1.