From b2807742f05f01f583632a982743388ba68ae2e5 Mon Sep 17 00:00:00 2001 From: Jos de Jong Date: Thu, 14 Dec 2023 10:05:23 +0100 Subject: [PATCH] fix: allow replacer and reviver to be `null` --- src/parse.test.ts | 7 +++++-- src/parse.ts | 4 +++- src/stringify.test.ts | 16 ++++++++++------ src/stringify.ts | 13 ++++++++----- src/utils.ts | 2 +- tsconfig.json | 1 + 6 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/parse.test.ts b/src/parse.test.ts index 2c80eb7..a9f2995 100644 --- a/src/parse.test.ts +++ b/src/parse.test.ts @@ -69,7 +69,7 @@ test('number', function () { expect(parse('23')).toEqual(lln('23')) expect(parse('0')).toEqual(lln('0')) expect(parse('0e+2')).toEqual(lln('0e+2')) - expect(parse('0e+2').valueOf()).toEqual(0) + expect((parse('0e+2') as LosslessNumber).valueOf()).toEqual(0) expect(parse('0.0')).toEqual(lln('0.0')) expect(parse('-0')).toEqual(lln('-0')) expect(parse('2.3')).toEqual(lln('2.3')) @@ -190,7 +190,8 @@ test('reviver - invoke callbacks with key/value and correct context', function ( // convert LosslessNumbers to numbers for easy comparison with native JSON function toRegularJSON(json: unknown) { - return JSON.parse(stringify(json)) + const str = stringify(json) + return str !== undefined ? JSON.parse(str) : undefined } function reviver(key: string, value: unknown): unknown { @@ -211,6 +212,8 @@ test('reviver - invoke callbacks with key/value and correct context', function ( const logsActual: Log[] = [] parse(text, function (key, value) { logsActual.push({ + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error context: toRegularJSON(this), key, value: toRegularJSON(value) diff --git a/src/parse.ts b/src/parse.ts index 29cc174..8f7db23 100644 --- a/src/parse.ts +++ b/src/parse.ts @@ -27,7 +27,7 @@ import { GenericObject } from './types' */ export function parse( text: string, - reviver?: Reviver, + reviver?: Reviver | null, parseNumber: NumberParser = parseLosslessNumber ): unknown { let i = 0 @@ -57,6 +57,7 @@ export function parse( const key = parseString() if (key === undefined) { throwObjectKeyExpected() + return // To make TS happy } skipWhitespace() @@ -65,6 +66,7 @@ export function parse( if (value === undefined) { throwObjectValueExpected() + return // To make TS happy } // TODO: test deep equal instead of strict equal diff --git a/src/stringify.test.ts b/src/stringify.test.ts index 79b2f9f..e53d228 100644 --- a/src/stringify.test.ts +++ b/src/stringify.test.ts @@ -1,6 +1,6 @@ import { test, expect } from 'vitest' import Decimal from 'decimal.js' -import { LosslessNumber, stringify } from './index' +import { LosslessNumber, NumberStringifier, stringify } from './index' import type { GenericObject } from './types' // helper function to create a lossless number @@ -10,6 +10,8 @@ function lln(value: string) { test('stringify', function () { expect(stringify(undefined)).toEqual(undefined) + expect(stringify(function () {})).toEqual(undefined) + expect(stringify(Symbol('test'))).toEqual(undefined) expect(stringify(null)).toEqual('null') @@ -106,9 +108,9 @@ test('stringify Date', function () { }) test('stringify Decimal', function () { - const decimalStringifier = { - test: (value: string) => Decimal.isDecimal(value), - stringify: (value: string) => value.toString() + const decimalStringifier: NumberStringifier = { + test: (value: unknown) => Decimal.isDecimal(value), + stringify: (value: unknown) => (value as Decimal).toString() } const numberStringifiers = [decimalStringifier] @@ -135,8 +137,8 @@ test('should not have a .toJSON method implemented', function () { test('should throw an error when the output of a number stringifier is not a number', function () { const wrongStringifier = { - test: (value: string) => typeof value === 'number', - stringify: (value: string) => 'oopsie' + value // <-- does not return a valid number + test: (value: unknown) => typeof value === 'number', + stringify: (value: unknown) => 'oopsie' + value // <-- does not return a valid number } expect(() => stringify([4], undefined, undefined, [wrongStringifier])).toThrow( @@ -207,6 +209,8 @@ test('stringify with replacer function', function () { const logs: Log[] = [] stringify(json, function (key, value) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error logs.push({ context: this, key, value }) return value }) diff --git a/src/stringify.ts b/src/stringify.ts index fa50b5c..d29c5da 100644 --- a/src/stringify.ts +++ b/src/stringify.ts @@ -37,7 +37,7 @@ import { isNumber } from './utils.js' */ export function stringify( value: unknown, - replacer?: Replacer, + replacer?: Replacer | null, space?: number | string, numberStringifiers?: NumberStringifier[] ): string | undefined { @@ -51,11 +51,11 @@ export function stringify( /** * Stringify a value */ - function stringifyValue(value: unknown, indent: string): string | undefined { + function stringifyValue(value: unknown, indent: string | undefined): string | undefined { if (Array.isArray(numberStringifiers)) { const stringifier = numberStringifiers.find((item) => item.test(value)) if (stringifier) { - const str = stringifier.stringify(value) + const str: unknown = stringifier.stringify(value) if (typeof str !== 'string' || !isNumber(str)) { throw new Error( 'Invalid JSON number: ' + @@ -109,7 +109,7 @@ export function stringify( /** * Stringify an array */ - function stringifyArray(array: Array, indent: string): string { + function stringifyArray(array: Array, indent: string | undefined): string { if (array.length === 0) { return '[]' } @@ -143,7 +143,10 @@ export function stringify( /** * Stringify an object */ - function stringifyObject(object: GenericObject, indent: string): string { + function stringifyObject( + object: GenericObject, + indent: string | undefined + ): string | undefined { if (typeof object.toJSON === 'function') { return stringify(object.toJSON(), replacer, space, undefined) } diff --git a/src/utils.ts b/src/utils.ts index 22840fe..b381b58 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -110,7 +110,7 @@ export function toSafeNumberOrThrow( ? unsafeReason && unsafeReason !== UnsafeNumberReason.truncate_float : unsafeReason ) { - const unsafeReasonText = unsafeReason.replace(/_\w+$/, '') + const unsafeReasonText = unsafeReason?.replace(/_\w+$/, '') throw new Error( 'Cannot safely convert to number: ' + `the value '${value}' would ${unsafeReasonText} and become ${number}` diff --git a/tsconfig.json b/tsconfig.json index b96d254..cefdd70 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,6 +8,7 @@ "esModuleInterop": true, "allowJs": false, "checkJs": false, + "strict": true, "noImplicitAny": true, "declaration": true, "declarationDir": "lib/types",