Skip to content

Commit b1e3834

Browse files
committed
fix: improve type definition
The current typescript definition always indicated a string as return type. That is faulty for values such as `undefined` or symbol values. The new return type determins the correct return type depending on the input type. Fixes: #35
1 parent 73a49c2 commit b1e3834

File tree

6 files changed

+58
-61
lines changed

6 files changed

+58
-61
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Changelog
22

3+
## v2.4.1
4+
5+
- More precise TypeScript types. The return type is now either `string`, `undefined` or `string | undefined` depending on the input.
6+
37
## v2.4.0
48

59
- Added `strict` option to verify that the passed in objects are fully compatible with JSON without removing information. If not, an error is thrown.

esm/wrapper.d.ts

+1-19
Original file line numberDiff line numberDiff line change
@@ -1,19 +1 @@
1-
export function stringify(value: any, replacer?: (key: string, value: any) => any, space?: string | number): string;
2-
export function stringify(value: any, replacer?: (number | string)[] | null, space?: string | number): string;
3-
4-
export interface StringifyOptions {
5-
bigint?: boolean,
6-
circularValue?: string | null | TypeErrorConstructor | ErrorConstructor,
7-
deterministic?: boolean,
8-
maximumBreadth?: number,
9-
maximumDepth?: number,
10-
strict?: boolean,
11-
}
12-
13-
export namespace stringify {
14-
export function configure(options: StringifyOptions): typeof stringify;
15-
}
16-
17-
export function configure(options: StringifyOptions): typeof stringify;
18-
19-
export default stringify;
1+
export * from '../index.d'

index.d.ts

+8-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
export function stringify(value: any, replacer?: (key: string, value: any) => any, space?: string | number): string;
2-
export function stringify(value: any, replacer?: (number | string)[] | null, space?: string | number): string;
1+
export type Replacer = (number | string)[] | null | undefined | ((key: string, value: unknown) => string | number | boolean | null)
2+
3+
export function stringify(value: undefined | symbol | ((...args: unknown[]) => unknown), replacer?: Replacer, space?: string | number): undefined
4+
export function stringify(value: string | number | unknown[] | null | boolean | object, replacer?: Replacer, space?: string | number): string
5+
export function stringify(value: unknown, replacer?: ((key: string, value: unknown) => unknown) | (number | string)[] | null | undefined, space?: string | number): string | undefined
36

47
export interface StringifyOptions {
58
bigint?: boolean,
@@ -11,9 +14,9 @@ export interface StringifyOptions {
1114
}
1215

1316
export namespace stringify {
14-
export function configure(options: StringifyOptions): typeof stringify;
17+
export function configure(options: StringifyOptions): typeof stringify
1518
}
1619

17-
export function configure(options: StringifyOptions): typeof stringify;
20+
export function configure(options: StringifyOptions): typeof stringify
1821

19-
export default stringify;
22+
export default stringify

index.js

+5-6
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,8 @@ exports.configure = configure
1919

2020
module.exports = stringify
2121

22-
// eslint-disable-next-line
22+
// eslint-disable-next-line no-control-regex
2323
const strEscapeSequencesRegExp = /[\u0000-\u001f\u0022\u005c\ud800-\udfff]|[\ud800-\udbff](?![\udc00-\udfff])|(?:[^\ud800-\udbff]|^)[\udc00-\udfff]/
24-
// eslint-disable-next-line
2524
const strEscapeSequencesReplacer = new RegExp(strEscapeSequencesRegExp, 'g')
2625

2726
// Escaped special characters. Use empty strings to fill up unused entries.
@@ -130,7 +129,7 @@ function stringifyTypedArray (array, separator, maximumBreadth) {
130129
}
131130

132131
function getCircularValueOption (options) {
133-
if (options && hasOwnProperty.call(options, 'circularValue')) {
132+
if (hasOwnProperty.call(options, 'circularValue')) {
134133
const circularValue = options.circularValue
135134
if (typeof circularValue === 'string') {
136135
return `"${circularValue}"`
@@ -152,7 +151,7 @@ function getCircularValueOption (options) {
152151

153152
function getBooleanOption (options, key) {
154153
let value
155-
if (options && hasOwnProperty.call(options, key)) {
154+
if (hasOwnProperty.call(options, key)) {
156155
value = options[key]
157156
if (typeof value !== 'boolean') {
158157
throw new TypeError(`The "${key}" argument must be of type boolean`)
@@ -163,7 +162,7 @@ function getBooleanOption (options, key) {
163162

164163
function getPositiveIntegerOption (options, key) {
165164
let value
166-
if (options && hasOwnProperty.call(options, key)) {
165+
if (hasOwnProperty.call(options, key)) {
167166
value = options[key]
168167
if (typeof value !== 'number') {
169168
throw new TypeError(`The "${key}" argument must be of type number`)
@@ -196,7 +195,7 @@ function getUniqueReplacerSet (replacerArray) {
196195
}
197196

198197
function getStrictOption (options) {
199-
if (options && hasOwnProperty.call(options, 'strict')) {
198+
if (hasOwnProperty.call(options, 'strict')) {
200199
const value = options.strict
201200
if (typeof value !== 'boolean') {
202201
throw new TypeError('The "strict" argument must be of type boolean')

test.js

+38-31
Original file line numberDiff line numberDiff line change
@@ -346,13 +346,17 @@ test('replacer removing all elements and indentation', function (assert) {
346346
test('array replacer', function (assert) {
347347
const replacer = ['f', 1, null]
348348
const obj = { f: null, null: true, 1: false }
349-
// The null element will be removed!
349+
// The null element will be ignored!
350+
// @ts-expect-error
350351
const expected = JSON.stringify(obj, replacer)
352+
// @ts-expect-error
351353
let actual = stringify(obj, replacer)
352354
assert.equal(actual, expected)
353355

356+
// @ts-expect-error
354357
obj.f = obj
355358

359+
// @ts-expect-error
356360
actual = stringify({ toJSON () { return obj } }, replacer)
357361
assert.equal(actual, expected.replace('null', '"[Circular]"'))
358362

@@ -374,7 +378,9 @@ test('array replacer and indentation', function (assert) {
374378
const replacer = ['f', 1, null]
375379
const obj = { f: null, null: true, 1: [false, -Infinity, 't'] }
376380
// The null element will be removed!
381+
// @ts-expect-error
377382
const expected = JSON.stringify(obj, replacer, 2)
383+
// @ts-expect-error
378384
const actual = stringify(obj, replacer, 2)
379385
assert.equal(actual, expected)
380386
assert.end()
@@ -484,16 +490,16 @@ test('object with undefined values', function (assert) {
484490
})
485491

486492
test('undefined values and indented', function (assert) {
487-
let obj = { a: 1, c: undefined, b: 'hello' }
493+
const obj1 = { a: 1, c: undefined, b: 'hello' }
488494

489-
let expected = JSON.stringify(obj, null, 2)
490-
let actual = stringify(obj, null, 2)
495+
let expected = JSON.stringify(obj1, null, 2)
496+
let actual = stringify(obj1, null, 2)
491497
assert.equal(actual, expected)
492498

493-
obj = { b: 'hello', a: undefined, c: 1 }
499+
const obj2 = { b: 'hello', a: undefined, c: 1 }
494500

495-
expected = JSON.stringify(obj)
496-
actual = stringify(obj)
501+
expected = JSON.stringify(obj2)
502+
actual = stringify(obj2)
497503
assert.equal(actual, expected)
498504

499505
assert.end()
@@ -516,6 +522,7 @@ test('bigint option', function (assert) {
516522
assert.equal(actualBigInt, expectedBigInt)
517523
assert.equal(actualDefault, expectedBigInt)
518524

525+
// @ts-expect-error
519526
assert.throws(() => stringify.configure({ bigint: null }), /bigint/)
520527

521528
assert.end()
@@ -620,11 +627,11 @@ test('non-deterministic with replacer', function (assert) {
620627
const keys = Object.keys(obj)
621628

622629
const expected = stringify(obj, ['b', 'c', 'd', 'e'])
623-
let actual = stringifyNonDeterministic(obj, keys)
624-
assert.equal(actual, expected)
630+
const actualA = stringifyNonDeterministic(obj, keys)
631+
assert.equal(actualA, expected)
625632

626-
actual = stringifyNonDeterministic(obj, (k, v) => v)
627-
assert.equal(actual, expected)
633+
const actualB = stringifyNonDeterministic(obj, (k, v) => v)
634+
assert.equal(actualB, expected)
628635

629636
assert.end()
630637
})
@@ -654,20 +661,20 @@ test('check small typed arrays with extra properties', function (assert) {
654661
// @ts-expect-error
655662
obj.foo = true
656663
let expected = JSON.stringify(obj)
657-
let actual = stringify(obj)
658-
assert.equal(actual, expected)
664+
const actualA = stringify(obj)
665+
assert.equal(actualA, expected)
659666

660667
expected = JSON.stringify(obj, null, 2)
661-
actual = stringify(obj, null, 2)
662-
assert.equal(actual, expected)
668+
const actualB = stringify(obj, null, 2)
669+
assert.equal(actualB, expected)
663670

664671
expected = JSON.stringify(obj, ['foo'])
665-
actual = stringify(obj, ['foo'])
666-
assert.equal(actual, expected)
672+
const actualC = stringify(obj, ['foo'])
673+
assert.equal(actualC, expected)
667674

668675
expected = JSON.stringify(obj, (a, b) => b)
669-
actual = stringify(obj, (a, b) => b)
670-
assert.equal(actual, expected)
676+
const actualD = stringify(obj, (a, b) => b)
677+
assert.equal(actualD, expected)
671678

672679
assert.end()
673680
})
@@ -972,26 +979,26 @@ test('show skipped keys even non were serliazable', (assert) => {
972979

973980
const input = { a: Symbol('ignored'), b: Symbol('ignored') }
974981

975-
let actual = serialize(input)
982+
const actual1 = serialize(input)
976983
let expected = '{"...":"1 item not stringified"}'
977-
assert.equal(actual, expected)
984+
assert.equal(actual1, expected)
978985

979-
actual = serialize(input, (a, b) => b)
980-
assert.equal(actual, expected)
986+
const actual2 = serialize(input, (a, b) => b)
987+
assert.equal(actual2, expected)
981988

982-
actual = serialize(input, null, 1)
989+
const actual3 = serialize(input, null, 1)
983990
expected = '{\n "...": "1 item not stringified"\n}'
984-
assert.equal(actual, expected)
991+
assert.equal(actual3, expected)
985992

986-
actual = serialize(input, (a, b) => b, 1)
987-
assert.equal(actual, expected)
993+
const actual4 = serialize(input, (a, b) => b, 1)
994+
assert.equal(actual4, expected)
988995

989-
actual = serialize(input, ['a'])
996+
const actual5 = serialize(input, ['a'])
990997
expected = '{}'
991-
assert.equal(actual, expected)
998+
assert.equal(actual5, expected)
992999

993-
actual = serialize(input, ['a', 'b', 'c'])
994-
assert.equal(actual, expected)
1000+
const actual6 = serialize(input, ['a', 'b', 'c'])
1001+
assert.equal(actual6, expected)
9951002

9961003
assert.end()
9971004
})

tsconfig.json

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
"importHelpers": false,
1313
"target": "esnext",
1414
"module": "CommonJS",
15+
"strict": true,
16+
"noImplicitAny": false,
1517
"lib": [
1618
"esnext",
1719
"dom"

0 commit comments

Comments
 (0)