n2words
Version:
Convert numbers to words in 70+ languages with zero dependencies. Supports BigInt, decimals, and browser/Node.js environments.
82 lines (71 loc) • 2.72 kB
JavaScript
/**
* Cardinal value parsing utility.
* Transforms user input (number, string, or bigint) into normalized components.
* Handles negatives, decimals, and scientific notation.
* @module parse-cardinal
*/
import { expandScientificNotation, hasScientificNotation, numberToString } from './expand-scientific.js'
/**
* Parses a value for cardinal conversion.
* Cardinals accept any numeric value: integers, decimals, negatives.
* @param {number|string|bigint} value - The value to parse
* @returns {{isNegative: boolean, integerPart: bigint, decimalPart?: string}} The parsed cardinal components
* @throws {TypeError} If value is not number, string, or bigint
* @throws {RangeError} If value is not finite
*/
export function parseCardinalValue(value) {
const type = typeof value
// BigInt: simplest case
if (typeof value === 'bigint') {
return value < 0n
? { isNegative: true, integerPart: -value }
: { isNegative: false, integerPart: value }
}
// Number: fast path for safe integers
if (typeof value === 'number') {
if (!Number.isFinite(value)) {
throw new RangeError('Number must be finite (NaN and Infinity are not supported)')
}
if (Number.isSafeInteger(value)) {
return value < 0
? { isNegative: true, integerPart: BigInt(-value) }
: { isNegative: false, integerPart: BigInt(value) }
}
return parseNumericString(numberToString(value))
}
// String input
if (typeof value === 'string') {
return parseNumericString(normalizeString(value))
}
throw new TypeError(
`Invalid value type: expected number, string, or bigint, received ${type}`,
)
}
/**
* Validates and normalizes a string numeric input.
* @param {string} value - The string to normalize
* @returns {string} The normalized numeric string
*/
function normalizeString(value) {
const trimmed = value.trim()
if (trimmed.length === 0 || Number.isNaN(Number(trimmed))) {
throw new RangeError(`Invalid number format: "${value}"`)
}
return hasScientificNotation(trimmed) ? expandScientificNotation(trimmed) : trimmed
}
/**
* Parses a normalized numeric string into components.
* @param {string} str - The normalized numeric string
* @returns {{isNegative: boolean, integerPart: bigint, decimalPart?: string}} The parsed numeric components
*/
function parseNumericString(str) {
const isNegative = str[0] === '-'
if (isNegative) str = str.slice(1)
const dotIndex = str.indexOf('.')
if (dotIndex === -1) {
return { isNegative, integerPart: BigInt(str) }
}
const integerStr = str.slice(0, dotIndex) || '0'
const decimalPart = str.slice(dotIndex + 1)
return { isNegative, integerPart: BigInt(integerStr), decimalPart }
}