Every CSV import I have debugged failed the same way. Not a crash, not a bad encoding — a parser that quietly made a type decision on my behalf, and made it wrong. Here are the four decisions worth making explicit, with the actual JS behaviour so you can check them yourself.
1. An empty cell becomes 0
Number("") // 0 — not NaN
Number(" ") // 0
So a row of { id: 1, score: "" } turns into score: 0, and the bug does not surface until someone looks at an average. A mean over "1, 2, 3, nothing" now divides by four. Worse, a filter like rows.filter(r => r.score > -1) starts including customers who never rated anything.
In CSV there is no null. An empty cell means "unknown", and the only honest JSON is null. Check cell === "" before you touch Number.
2. parseFloat reads a prefix, Number reads the whole string
parseFloat("12kg") // 12
Number("12kg") // NaN
Number("0x10") // 16 (hex literals are numbers)
Number("1e5") // 100000 (a SKU that looks like exponent notation)
Number("007") // 7 (a ZIP code)
Four different failure modes from two one-line functions. The lesson is not "pick the better one" — it is that the decision belongs to the column, not the characters. A column named order_id or zip is a string no matter how numeric it looks.
3. JSON numbers are doubles: the 15-digit wall
Number.MAX_SAFE_INTEGER is 9007199254740991 — sixteen digits.
JSON.parse('{"id":1234567890123456789}').id
// 1234567890126800
The damage happens at parse time, so stringifying afterwards cannot undo it. Any identifier longer than 15 digits — Stripe-style keys, snowflake IDs, IBANs — must cross the CSV→JSON boundary as a string.
4. Quoting is not optional, and Excel adds two non-standard extras
RFC 4180 allows a field to contain the delimiter, a doubled quote, and a newline. This is one record, not two:
id,note
1,"line1
line2"
split("\n") then split(",") gives you a phantom row and shifts every later column — the import "succeeds" with garbage. Then there are two Excel quirks that are not in the spec at all: a UTF-8 BOM before the first header (your key is \uFEFFid, not id) and, on locale-configured machines, a sep=; first line that becomes a header of its own.
CSV is untyped and JSON is typed, so the conversion is exactly where these assumptions get baked in. I keep a browser-side converter for this that only coerces plain decimals with no leading zero and leaves IDs alone: codetoolbox.pro/tools/csv-to-json.html — it runs locally, so customer exports never leave your machine. Decide per column, not per character.
Top comments (0)