Skip to content

Commit

Permalink
fix: #250 replace the JavaScriptValue type definition with `unknown…
Browse files Browse the repository at this point in the history
…`, which is more accurate

BREAKING CHANGE:

The usages of types `JavaScriptValue`, `JavaScriptObject`, `JavaScriptArray` and `JavaScriptPrimitive` must be replaced with `unknown`.
  • Loading branch information
josdejong authored Nov 1, 2023
2 parents 4e5937c + bb92e3f commit 6033fec
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 45 deletions.
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,25 +217,25 @@ The `LosslessJSON.parse()` function parses a string as JSON, optionally transfor

- **@param** `{string} text`
The string to parse as JSON. See the JSON object for a description of JSON syntax.
- **@param** `{(key: string, value: JSONValue) => JavaScriptValue} [reviver]`
- **@param** `{(key: string, value: JSONValue) => unknown} [reviver]`
If a function, prescribes how the value originally produced by parsing is transformed, before being returned.
- **@param** `{function(value: string) : JavaScriptValue} [parseNumber]`
- **@param** `{function(value: string) : unknown} [parseNumber]`
Pass an optional custom number parser. Input is a string, and the output can be any numeric value: `number`, `bigint`, `LosslessNumber`, or a custom `BigNumber` library. By default, all numeric values are parsed into a `LosslessNumber`.
- **@returns** `{JavaScriptValue}`
- **@returns** `{unknown}`
Returns the Object corresponding to the given JSON text.
- **@throws** Throws a SyntaxError exception if the string to parse is not valid JSON.

### stringify(value [, replacer [, space [, numberStringifiers]]])

The `LosslessJSON.stringify()` function converts a JavaScript value to a JSON string, optionally replacing values if a replacer function is specified, or optionally including only the specified properties if a replacer array is specified.

- **@param** `{JavaScriptValue} value`
- **@param** `{unknown} value`
The value to convert to a JSON string.
- **@param** `{((key: string, value: JavaScriptValue) => JSONValue) | Array.<string | number>} [replacer]`
- **@param** `{((key: string, value: unknown) => unknown) | Array.<string | number>} [replacer]`
A function that alters the behavior of the stringification process, or an array with strings or numbers that serve as a whitelist for selecting the properties of the value object to be included in the JSON string. If this value is `null` or not provided, all properties of the object are included in the resulting JSON string.
- **@param** `{number | string | undefined} [space]`
A `string` or `number` that is used to insert white space into the output JSON string for readability purposes. If this is a `number`, it indicates the number of space characters to use as white space. Values less than 1 indicate that no space should be used. If this is a `string`, the `string` is used as white space. If this parameter is not provided (or is `null`), no white space is used.
- **@param** `{Array<{test: (value: JavaScriptValue) => boolean, stringify: (value: JavaScriptValue) => string}>} [numberStringifiers]`
- **@param** `{Array<{test: (value: unknown) => boolean, stringify: (value: unknown) => string}>} [numberStringifiers]`
An optional list with additional number stringifiers, for example to serialize a `BigNumber`. The output of the function must be valid stringified JSON number. When `undefined` is returned, the property will be deleted from the object. The difference with using a `replacer` is that the output of a `replacer` must be JSON and will be stringified afterwards, whereas the output of the `numberStringifiers` is already stringified JSON.
- **@returns** `{string | undefined}`
Returns the string representation of the JSON object.
Expand Down
4 changes: 2 additions & 2 deletions src/parse.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { parseLosslessNumber } from './numberParsers.js'
import { revive } from './revive.js'
import type { NumberParser, Reviver } from './types'
import { GenericObject, JavaScriptValue } from './types'
import { GenericObject } from './types'

/**
* The LosslessJSON.parse() method parses a string as JSON, optionally transforming
Expand Down Expand Up @@ -29,7 +29,7 @@ export function parse(
text: string,
reviver?: Reviver,
parseNumber: NumberParser = parseLosslessNumber
): JavaScriptValue {
): unknown {
let i = 0
const value = parseValue()
expectValue(value)
Expand Down
6 changes: 1 addition & 5 deletions src/revive.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,7 @@ function reviveValue(
} else if (value && typeof value === 'object' && !isLosslessNumber(value)) {
// note the special case for LosslessNumber,
// we don't want to iterate over the internals of a LosslessNumber
return reviver.call(
context,
key,
reviveObject(value as unknown as GenericObject<unknown>, reviver)
)
return reviver.call(context, key, reviveObject(value as GenericObject<unknown>, reviver))
} else {
return reviver.call(context, key, value)
}
Expand Down
4 changes: 2 additions & 2 deletions src/stringify.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { GenericObject, Replacer, NumberStringifier, JavaScriptValue } from './types'
import type { GenericObject, NumberStringifier, Replacer } from './types'
import { isNumber } from './utils.js'

/**
Expand Down Expand Up @@ -36,7 +36,7 @@ import { isNumber } from './utils.js'
* @returns Returns the string representation of the JSON object.
*/
export function stringify(
value: JavaScriptValue,
value: unknown,
replacer?: Replacer,
space?: number | string,
numberStringifiers?: NumberStringifier[]
Expand Down
40 changes: 25 additions & 15 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,37 @@ export type JSONValue =
export type JSONObject = { [key: string]: JSONValue }
export type JSONArray = JSONValue[]

export type JavaScriptPrimitive = string | number | boolean | null | bigint | Date | unknown
export type JavaScriptValue =
| { [key: string]: JavaScriptValue } // object
| JavaScriptValue[] // array
| JavaScriptPrimitive
export type JavaScriptObject = { [key: string]: JavaScriptValue }
export type JavaScriptArray = JavaScriptValue[]
/**
* @deprecated use `unknown` instead
*/
export type JavaScriptPrimitive = unknown

export type Reviver = (key: string, value: JSONValue) => JavaScriptValue
/**
* @deprecated use `unknown` instead
*/
export type JavaScriptValue = unknown

export type NumberParser = (value: string) => JavaScriptValue
/**
* @deprecated use `unknown` instead
*/
export type JavaScriptObject = unknown

/**
* @deprecated use `unknown` instead
*/
export type JavaScriptArray = unknown

export type Reviver = (key: string, value: JSONValue) => unknown

export type NumberParser = (value: string) => unknown

export type Replacer =
| ((key: string, value: JavaScriptObject) => JSONValue | undefined)
| ((key: string, value: unknown) => unknown | undefined)
| Array<string | number>

export interface NumberStringifier {
test: (value: JavaScriptValue) => boolean
stringify: (value: JavaScriptValue) => string
test: (value: unknown) => boolean
stringify: (value: unknown) => string
}

export type GenericObject<T> = {
[key: string]: T
}
export type GenericObject<T> = Record<string, T>
12 changes: 6 additions & 6 deletions test/parse.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
reviveDate,
stringify
} from '../src'
import { GenericObject, JSONValue } from '../src/types'
import { GenericObject } from '../src/types'
import { isDeepEqual } from '../src/parse'

// helper function to create a lossless number
Expand All @@ -21,7 +21,7 @@ function expectDeepEqual(a: unknown, b: unknown) {
}

// turn a JavaScript object into plain JSON
function jsonify(obj: unknown): JSONValue {
function jsonify(obj: unknown): unknown {
return JSON.parse(JSON.stringify(obj))
}

Expand Down Expand Up @@ -115,7 +115,7 @@ test('reviver - replace values', function () {
}
}

function reviver(key: string, value: JSONValue) {
function reviver(key: string, value: unknown) {
return {
type: typeof value,
value
Expand All @@ -129,9 +129,9 @@ test('reviver - invoke callbacks with key/value and correct context', function (
const text = '{"a":123,"b":"str","c":null,"22":22,"d":false,"e":[1,2,3]}'

interface Log {
context: JSONValue
context: unknown
key: string
value: JSONValue
value: unknown
}

const expected: Log[] = [
Expand Down Expand Up @@ -192,7 +192,7 @@ test('reviver - invoke callbacks with key/value and correct context', function (
return JSON.parse(stringify(json))
}

function reviver(key: string, value: JSONValue) {
function reviver(key: string, value: unknown): unknown {
return key === 'd' ? undefined : key === '1' ? null : value
}

Expand Down
18 changes: 9 additions & 9 deletions test/stringify.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Decimal from 'decimal.js'
import { LosslessNumber, stringify } from '../src'
import type { GenericObject, JSONValue } from '../src/types'
import type { GenericObject } from '../src/types'

// helper function to create a lossless number
function lln(value: string) {
Expand Down Expand Up @@ -151,9 +151,9 @@ test('stringify with replacer function', function () {
const json: GenericObject<unknown> = { a: 123, b: 'str', c: null, d: false, e: [1, 2, 3] }

interface Log {
context: JSONValue
context: unknown
key: string
value: JSONValue
value: unknown
}

const expected: Log[] = [
Expand Down Expand Up @@ -206,8 +206,8 @@ test('stringify with replacer function', function () {

const logs: Log[] = []
stringify(json, function (key, value) {
logs.push({ context: this, key, value: value as JSONValue })
return value as JSONValue
logs.push({ context: this, key, value })
return value
})
expect(logs).toEqual(expected)

Expand All @@ -225,7 +225,7 @@ test('stringify with replacer function (2)', function () {

const expected = '{"a":"number:a:123","b":"string:b:str"}'

function replacer(key: string, value: unknown): JSONValue {
function replacer(key: string, value: unknown): unknown {
if (key === 'c') {
return undefined
}
Expand All @@ -237,7 +237,7 @@ test('stringify with replacer function (2)', function () {
return 'string:' + key + ':' + value
}

return value as JSONValue
return value
}

expect(stringify(json, replacer)).toEqual(expected)
Expand All @@ -258,7 +258,7 @@ test('stringify with replacer Array', function () {
})

test('stringify with numeric space', function () {
const json: JSONValue = { a: 1, b: [1, 2, null, undefined, { c: 3 }], d: null }
const json: unknown = { a: 1, b: [1, 2, null, undefined, { c: 3 }], d: null }

const expected =
'{\n' +
Expand All @@ -282,7 +282,7 @@ test('stringify with numeric space', function () {
})

test('stringify with string space', function () {
const json: JSONValue = { a: 1, b: [1, 2, null, undefined, { c: 3 }], d: null }
const json: unknown = { a: 1, b: [1, 2, null, undefined, { c: 3 }], d: null }

const expected =
'{\n' +
Expand Down

0 comments on commit 6033fec

Please sign in to comment.