How Removing isNumber from Dependencies Saved 440GB of Weekly Bandwidth
I recently came across something interesting merge request by replacing dependency isNumber. It was surprising to realize that there is no such universal method for determining a number in a variable in the basic concept JavaScript. And this problem gave birth to an npm repository isNumber c almost 72 million weekly downloads for September 2024. But is it worth using a tiny dependency in your project once again? I suggest taking a look at the solution presented in the previously mentioned mr.
Analysis of the solution
The concept of isNumber is simple: a function that takes the variable being defined as an argument should output true or false; true if yes, false if no. It's that simple!
This is what this function looks like in implementation:
const isNumber = (v) => (typeof v === "number" && v - v === 0) || (typeof v === "string" && Number.isFinite(+v) && v.trim() !== "");
Looks heavy, doesn't it? Let's analyze each section of code and find out if this function solves our problem.
The function takes an argument v
(variable), which is what we will be working with.
The essence of the work consists mostly in catching any output of the type number
in this variable.
The first part checks the numeric value directly (even the same Infinite)
WITH
typeof v === "number"
everything is quite clear. Direct type checkingv - v === 0
will return true in the case of a number, since if we were working, for example, with a string, then such an expression would returnNaN
. But in the case of arrays this would not work and this expression would also give us 0. Let's move on.
The second part checks for strings that can be converted to numbers.
typeof v === "string"
checks if a variable isv
line.Number.isFinite(+v)
– here is a linev
is reduced to a number using unary operator+
. If after the transformation the string is a finite number (that is, notNaN
NotInfinite
), ThatNumber.isFinite
will returntrue
. This is important to filter out strings that cannot be converted to numbers correctly (e.g."abc"
or empty lines).
Unarythat is, applied to one value,
+
does nothing with numeric values. But if the variable is not a number, unary plus converts it to a number.
v.trim() !== ""
— this check removes spaces from the beginning and end of the line and checks that the line is not empty. This is necessary in order to filter out lines consisting only of spaces, which can be converted to0
but in practice are not correct representations of numbers.
General view
To summarize, we see two parts that perform the following tasks:
Checking numbers (
typeof v === "number" && v - v === 0
): It is important not only to check the type, but also to excludeNaN
which has the type"number"
but is not actually a number. Check viav - v === 0
– this is an effective way to eliminateNaN
since for all other numbers the difference of a number with itself is always zero.Checking strings (
typeof v === "string" && Number.isFinite(+v) && v.trim() !== ""
): Strings that can be converted to numbers are common in web development, especially when working with forms or APIs where data is passed as strings. It is important to handle strings that can be converted to numbers correctly and to filter out empty strings or strings that consist only of spaces. UsageNumber.isFinite
Allows you to check whether the converted value is a finite number.
So what's up with the repository?
Plastic bag to-regex-range
which was discussed at the beginning of the article, has 43 million downloads at the time of the merge request, and the initial package size is 33Kb. It is important to note that the main logic of this package consists of one medium-sized .js file with a couple of dependencies.
After replacing the package isNumber
on their own solution from dependencies found that to-regex-range
became lighter by 10Kb. But this figure does not seem to matter, but the fact remains that in the end, a small change led to a reduction in weekly download traffic on 440 gigabytesreducing it from 1.5TB to 1.0TB. That already sounds really impressive.
Package size report
===================
Package info for "to-regex-range@5.0.1": 33 kB
Released: 2019-04-07 06:04:37.03 +0000 UTC (277w2d ago)
Downloads last week: 43,837,006
Estimated traffic last week: 1.5 TB
Removed dependencies:
- is-number@7.0.0: 10 kB (30.06%)
Downloads last week: 43,875,245
Downloads last week from "to-regex-range@5.0.1": 43,837,006 (99.91%)
Estimated traffic last week: 440 GB
Estimated traffic from "to-regex-range@5.0.1": 440 GB (99.91%)
Estimated package size: 33 kB → 23 kB (69.94%)
Estimated traffic over a week: 1.5 TB → 1.0 TB (440 GB saved)