From 0b2a5621b80a94ba16a53475e0b611c214281df1 Mon Sep 17 00:00:00 2001 From: TheoryOfNekomata Date: Mon, 21 Aug 2023 02:27:27 +0800 Subject: [PATCH] Enhance validation Add handling to extreme/sentinel values. --- packages/core/src/converter.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/packages/core/src/converter.ts b/packages/core/src/converter.ts index a175ff3..e9e5dca 100644 --- a/packages/core/src/converter.ts +++ b/packages/core/src/converter.ts @@ -75,6 +75,10 @@ export const stringify = < throw new TypeError(`Value must be a string, number, or bigint. Received: ${typeof value}`); } + if (typeof value === 'number' && Number.isNaN(value)) { + return 'NaN'; + } + const valueStr = value.toString().replace(/\s/g, ''); const { system = enUS.shortCount, stringifyGroupsOptions, mergeTokensOptions } = options; @@ -110,7 +114,16 @@ export interface ParseOptions { * @returns AllowedValue The numeric equivalent of the value. */ export const parse = (value: string, options = {} as ParseOptions) => { - const { system = enUS.shortCount, type = 'string' } = options; + const { system = enUS.shortCount, type: typeRaw = 'string' } = options; + const type = typeRaw.trim().toLowerCase() as ParseResult; + + if (!((ALLOWED_PARSE_RESULT_TYPES as unknown as string[]).includes(type))) { + throw new TypeError(`Return type must be a string, number, or bigint. Received: ${type}`); + } + + if (value.trim().toLowerCase() === 'nan') { + return type === 'number' ? NaN : 'NaN'; + } const tokens = system.tokenize(value); const { groups, negative } = system.parseGroups(tokens); @@ -124,6 +137,7 @@ export const parse = (value: string, options = {} as ParseOptions) => { const exponentValue = Number(exponent); const integerValue = Number(integer); + // max safe integer: 9.007199254740991e+15 const [maxSafeIntegerSignificand, maxSafeIntegerExponent] = Number.MAX_SAFE_INTEGER.toExponential().split('e'); if ( exponentValue >= Number(maxSafeIntegerExponent) @@ -136,6 +150,7 @@ export const parse = (value: string, options = {} as ParseOptions) => { return stringValue; } + // epsilon: 2.220446049250313e-16 const [epsilonSignificand, epsilonExponent] = Number.EPSILON.toExponential().split('e'); if ( exponentValue <= Number(epsilonExponent)