import { enUS } from './systems'; import { StringifySystem } from './common'; /** * Negative symbol. */ const NEGATIVE_SYMBOL = '-' as const; /** * Allowed value type for {@link stringify}. */ type AllowedValue = string | number | bigint; /** * Array of allowed types for {@link parse}. */ const ALLOWED_PARSE_RESULT_TYPES = [ 'string', 'number', 'bigint', ] as const; /** * Allowed type for {@link parse}. */ type ParseResult = typeof ALLOWED_PARSE_RESULT_TYPES[number]; /** * Options to use when converting a value to a string. */ export interface StringifyOptions { /** * The system to use when converting a value to a string. * * Defaults to en-US (American short count). */ system?: StringifySystem; /** * Options to use when making a group. This is used to override the default options for a group. */ makeGroupOptions?: Record; } /** * Converts a numeric value to its name. * @param value - The value to convert. * @param options - Options to use when converting a value to its name. * @returns string The name of the value. */ export const stringify = ( value: AllowedValue, options = {} as StringifyOptions, ): string => { if (!( (ALLOWED_PARSE_RESULT_TYPES as unknown as string[]) .includes(typeof (value as unknown)) )) { throw new TypeError(`Value must be a string, number, or bigint. Received: ${typeof value}`); } const valueStr = value.toString().replace(/\s/g, ''); const { system = enUS, makeGroupOptions } = options; if (valueStr.startsWith(NEGATIVE_SYMBOL)) { return system.makeNegative(stringify(valueStr.slice(NEGATIVE_SYMBOL.length), options)); } const groups = system.group(valueStr); const groupNames = system.makeGroups(groups, makeGroupOptions); return system.finalize(groupNames); }; /** * Options to use when parsing a name of a number to its numeric equivalent. */ export interface ParseOptions { /** * The system to use when parsing a name of a number to its numeric equivalent. * * Defaults to en-US (American short count). */ system?: StringifySystem; /** * The type to parse the value as. */ type?: ParseResult; } /** * Parses a name of a number to its numeric equivalent. * @param value - The value to parse. * @param options - Options to use when parsing a name of a number to its numeric equivalent. * @returns AllowedValue The numeric equivalent of the value. */ export const parse = (value: string, options = {} as ParseOptions) => { const { system = enUS, type = 'string' } = options; const tokens = system.tokenize(value); const groups = system.parseGroups(tokens); const stringValue = system.combineGroups(groups); switch (type) { case 'number': // Precision might be lost here. Use bigint when not using fractional parts. return Number(stringValue); case 'bigint': return BigInt(stringValue); default: break; } return stringValue; };