]> git.ardis.lu - csvio/commitdiff
Refactor `expandScientificNotation` to throw errors instead of using sentinel `null...
authorArdis Lu <ardisglu@gmail.com>
Sun, 25 May 2025 04:45:53 +0000 (21:45 -0700)
committerArdis Lu <ardisglu@gmail.com>
Sun, 25 May 2025 04:45:53 +0000 (21:45 -0700)
src/normalization.js
test/normalization.test.js

index 3debefe00a9ebb5621306d5b484a490c63261de9..a241eb13449ef89334ea87e46a93be79c2226019 100644 (file)
@@ -24,26 +24,31 @@ export function toCamelCase(str) {
  * 
  * @param {string} str A string representing a number expressed using scientific notation (e.g., `"1E+18"`).
  * @param {boolean} truncate If `true`, decimals will be truncated in the expanded string. Defaults to `false`.
- * @returns {string|null} A string representing the normal form of the number (e.g., `"1000000000000000000"`), or `null`
- * if the input could not be parsed.
+ * @returns {string} A string representing the normal form of the number (e.g., `"1000000000000000000"`).
+ * @throws {TypeError} If `str` is not a `string`, a `TypeError` will be thrown.
+ * @throws {SyntaxError} If `str` is not valid scientific notation, a `SyntaxError` will be thrown.
  */
 export function expandScientificNotation(str, truncate = false) {
+  if (typeof str !== 'string') {
+    throw new TypeError(`Expected a string, but received ${typeof str}.`);
+  }
+  const [mantissa, exponent] = str.split('E');
+  if (mantissa === undefined || exponent === undefined) {
+    throw new SyntaxError(`Unable to parse string "${str}" as scientific notation.`);
+  }
+  const e = Number(exponent);
   let expanded = null;
-  const [mantissa, exponent] = str.split?.('E') ?? [undefined, undefined];
-  if (mantissa !== undefined && exponent !== undefined) {
-    const e = Number(exponent);
-    if (mantissa.indexOf(DECIMAL_SEPARATOR) === -1) {
-      expanded = mantissa + '0'.repeat(e);
+  if (mantissa.indexOf(DECIMAL_SEPARATOR) === -1) {
+    expanded = mantissa + '0'.repeat(e);
+  }
+  else {
+    const [integer, decimal] = mantissa.split('.');
+    expanded = integer + decimal.padEnd(e, '0');
+    if (truncate) {
+      expanded = expanded.substring(0, e + 1);
     }
-    else {
-      const [integer, decimal] = mantissa.split('.');
-      expanded = integer + decimal.padEnd(e, '0');
-      if (truncate) {
-        expanded = expanded.substring(0, e + 1);
-      }
-      else if (mantissa.length - 2 > e) { // Must re-insert decimal
-        expanded = expanded.substring(0, e + 1) + '.' + expanded.substring(e + 1);
-      }
+    else if (mantissa.length - 2 > e) { // Must re-insert decimal
+      expanded = expanded.substring(0, e + 1) + '.' + expanded.substring(e + 1);
     }
   }
   return expanded;
@@ -228,7 +233,6 @@ export class CSVNormalizer extends TransformStream {
     // Excel converted to scientific notation
     try {
       const expanded = expandScientificNotation(original, true);
-      if (expanded === null) { throw new Error(); }
       return BigInt(expanded).toString();
     } catch { }
 
index 83986518815e511dc0837e4e7e63e4b4a1133e9b..92b8fe30c0c37b6b41c903a0561e892f98212910 100644 (file)
@@ -1,5 +1,5 @@
 import { suite, test } from 'node:test';
-import { deepStrictEqual } from 'node:assert/strict';
+import { deepStrictEqual, throws } from 'node:assert/strict';
 
 import { csvStreamEqualWritable, createCSVMockStream, assertConsole } from './utils.js';
 import { CSVReader } from '../src/core.js';
@@ -32,14 +32,14 @@ suite('toCamelCase', { concurrency: true }, () => {
 
 suite('expandScientificNotation', { concurrency: true }, () => {
   const vectors = [
-    { name: 'returns null for string integer', input: '123', output: null },
-    { name: 'returns null for string BigInt', input: '123n', output: null },
-    { name: 'returns null for string number', input: '123.456', output: null },
-    { name: 'returns null for string non-numeric', input: 'abc', output: null },
-    { name: 'returns null for number', input: 123, output: null },
-    { name: 'returns null for BigInt', input: 123n, output: null },
-    { name: 'returns null for array', input: [], output: null },
-    { name: 'returns null for object', input: {}, output: null },
+    { name: 'throws SyntaxError for string integer', input: '123', error: SyntaxError },
+    { name: 'throws SyntaxError for string BigInt', input: '123n', error: SyntaxError },
+    { name: 'throws SyntaxError for string number', input: '123.456', error: SyntaxError },
+    { name: 'throws SyntaxError for string non-numeric', input: 'abc', error: SyntaxError },
+    { name: 'throws TypeError for number', input: 123, error: TypeError },
+    { name: 'throws TypeError for BigInt', input: 123n, error: TypeError },
+    { name: 'throws TypeError for array', input: [], error: TypeError },
+    { name: 'throws TypeError for object', input: {}, error: TypeError },
     { name: 'returns expanded form for positive exponent', input: '1E18', output: '1000000000000000000' },
     { name: 'returns expanded form for positive exponent (with plus)', input: '1E+18', output: '1000000000000000000' },
     { name: 'returns expanded form for negative mantissa', input: '-1E18', output: '-1000000000000000000' },
@@ -51,9 +51,15 @@ suite('expandScientificNotation', { concurrency: true }, () => {
     { name: 'truncates when decimals greater than exponent by 1', input: '1.111E2', truncate: true, output: '111' },
     { name: 'truncates when decimals greater than exponent by 2', input: '1.1111E2', truncate: true, output: '111' },
   ];
-  for (const { name, input, truncate = false, output } of vectors) {
+  for (const { name, input, truncate = false, output, error } of vectors) {
     test(name, { concurrency: true }, () => {
-      deepStrictEqual(expandScientificNotation(input, truncate), output);
+      if (error !== undefined) {
+        // @ts-expect-error
+        throws(() => expandScientificNotation(input, truncate), error);
+      }
+      else {
+        deepStrictEqual(expandScientificNotation(input, truncate), output);
+      }
     });
   }
 });
This page took 0.046622 seconds and 4 git commands to generate.