diff --git a/src/predicate/index.ts b/src/predicate/index.ts index 344fe617..140a031d 100644 --- a/src/predicate/index.ts +++ b/src/predicate/index.ts @@ -7,9 +7,7 @@ export { isEqualWith } from './isEqualWith.ts'; export { isError } from './isError.ts'; export { isFile } from './isFile.ts'; export { isFunction } from './isFunction.ts'; -export { isJSONArray } from './isJSONArray.ts'; -export { isJSONObject } from './isJSONObject.ts'; -export { isJSONValue } from './isJSONValue.ts'; +export { isJSONArray, isJSONObject, isJSONValue } from './isJSONValue.ts'; export { isLength } from './isLength.ts'; export { isMap } from './isMap.ts'; export { isNil } from './isNil.ts'; diff --git a/src/predicate/isJSONArray.spec.ts b/src/predicate/isJSONArray.spec.ts deleted file mode 100644 index 7113c87f..00000000 --- a/src/predicate/isJSONArray.spec.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { describe, expect, it } from 'vitest'; -import { isJSONArray } from './isJSONArray.ts'; - -describe('isJSONArray', () => { - it('should return true for valid JSON arrays', () => { - expect(isJSONArray([1, 2, 3])).toBe(true); - expect(isJSONArray(['string', null, true])).toBe(true); - expect(isJSONArray([{ key: 'value' }, [1, 2], null])).toBe(true); - expect(isJSONArray([])).toBe(true); - }); - - it('should return false for invalid JSON arrays', () => { - expect(isJSONArray('not an array')).toBe(false); - expect(isJSONArray(123)).toBe(false); - expect(isJSONArray(null)).toBe(false); - expect(isJSONArray(undefined)).toBe(false); - expect(isJSONArray({})).toBe(false); - expect(isJSONArray([1, 2, () => {}])).toBe(false); - expect(isJSONArray([1, 2, new Date()])).toBe(false); - }); -}); diff --git a/src/predicate/isJSONArray.ts b/src/predicate/isJSONArray.ts deleted file mode 100644 index 0de1c556..00000000 --- a/src/predicate/isJSONArray.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { isJSONValue } from './isJSONValue.ts'; - -/** - * Checks if a given value is a valid JSON array. - * - * A valid JSON array is defined as an array where all items are valid JSON values. - * - * @param {unknown} value - The value to check. - * @returns {value is any[]} - True if the value is a valid JSON array, otherwise false. - * - * @example - * console.log(isJSONArray([1, 2, 3])); // true - * console.log(isJSONArray(["string", null, true])); // true - * console.log(isJSONArray([1, 2, () => {}])); // false - * console.log(isJSONArray("not an array")); // false - */ -export function isJSONArray(value: unknown): value is any[] { - if (!Array.isArray(value)) { - return false; - } - - return value.every(item => isJSONValue(item)); -} diff --git a/src/predicate/isJSONObject.spec.ts b/src/predicate/isJSONObject.spec.ts deleted file mode 100644 index 8d3a9f34..00000000 --- a/src/predicate/isJSONObject.spec.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { describe, expect, it } from 'vitest'; -import { isJSONObject } from './isJSONObject.ts'; - -describe('isJSONObject', () => { - it('isJSONObject should return true for valid JSON objects', () => { - expect(isJSONObject({ a: 1, b: 'es-toolkit' })).toBe(true); - expect(isJSONObject({})).toBe(true); - expect(isJSONObject({ nested: { boolean: true, array: [1, 2, 3], string: 'test', null: null } })).toBe(true); - }); - - it('isJSONObject should return false for not valid value', () => { - expect(isJSONObject(null)).toBe(false); - expect(isJSONObject(undefined)).toBe(false); - expect(isJSONObject('string')).toBe(false); - expect(isJSONObject(123)).toBe(false); - expect(isJSONObject(true)).toBe(false); - expect(isJSONObject([1, 2, 3])).toBe(false); - expect(isJSONObject({ date: new Date() })).toBe(false); - expect(isJSONObject({ func: () => {} })).toBe(false); - expect(isJSONObject({ regexp: /test/ })).toBe(false); - expect(isJSONObject({ undefinedProperty: undefined })).toBe(false); - expect(isJSONObject({ symbolProperty: Symbol('test') })).toBe(false); - expect(isJSONObject({ nested: { a: function* () {} } })).toBe(false); - }); - - it('isJSONObject should return false when key is not a string', () => { - expect(isJSONObject({ [Symbol('a')]: 'a' })).toBe(false); - }); -}); diff --git a/src/predicate/isJSONObject.ts b/src/predicate/isJSONObject.ts deleted file mode 100644 index 1089b2ec..00000000 --- a/src/predicate/isJSONObject.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { isJSONValue } from './isJSONValue.ts'; -import { isPlainObject } from './isPlainObject.ts'; - -/** - * Checks if a value is a JSON object. - * - * A valid JSON object is defined as an object with string keys and valid JSON values. - * - * @param {unknown} obj The value to check. - * @returns {obj is Record} True if `obj` is a JSON object, false otherwise. - * - * @example - * isJSONObject({ nested: { boolean: true, array: [1, 2, 3], string: 'test', null: null } }); // true - * isJSONObject({ regexp: /test/ }); // false - * isJSONObject(123); // false - */ -export function isJSONObject(obj: unknown): obj is Record { - if (!isPlainObject(obj)) { - return false; - } - - const keys = Reflect.ownKeys(obj); - - for (let i = 0; i < keys.length; i++) { - const key = keys[i]; - const value = obj[key]; - - if (typeof key !== 'string') { - return false; - } - - if (!isJSONValue(value)) { - return false; - } - } - - return true; -} diff --git a/src/predicate/isJSONValue.spec.ts b/src/predicate/isJSONValue.spec.ts index 0e3186fc..0b048252 100644 --- a/src/predicate/isJSONValue.spec.ts +++ b/src/predicate/isJSONValue.spec.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { isJSONValue } from './isJSONValue.ts'; +import { isJSONArray, isJSONObject, isJSONValue } from './isJSONValue.ts'; describe('isJSONValue', () => { it('should return true for null', () => { @@ -34,3 +34,49 @@ describe('isJSONValue', () => { expect(isJSONValue(Symbol('symbol'))).toBe(false); }); }); + +describe('isJSONArray', () => { + it('should return true for valid JSON arrays', () => { + expect(isJSONArray([1, 2, 3])).toBe(true); + expect(isJSONArray(['string', null, true])).toBe(true); + expect(isJSONArray([{ key: 'value' }, [1, 2], null])).toBe(true); + expect(isJSONArray([])).toBe(true); + }); + + it('should return false for invalid JSON arrays', () => { + expect(isJSONArray('not an array')).toBe(false); + expect(isJSONArray(123)).toBe(false); + expect(isJSONArray(null)).toBe(false); + expect(isJSONArray(undefined)).toBe(false); + expect(isJSONArray({})).toBe(false); + expect(isJSONArray([1, 2, () => {}])).toBe(false); + expect(isJSONArray([1, 2, new Date()])).toBe(false); + }); +}); + +describe('isJSONObject', () => { + it('isJSONObject should return true for valid JSON objects', () => { + expect(isJSONObject({ a: 1, b: 'es-toolkit' })).toBe(true); + expect(isJSONObject({})).toBe(true); + expect(isJSONObject({ nested: { boolean: true, array: [1, 2, 3], string: 'test', null: null } })).toBe(true); + }); + + it('isJSONObject should return false for not valid value', () => { + expect(isJSONObject(null)).toBe(false); + expect(isJSONObject(undefined)).toBe(false); + expect(isJSONObject('string')).toBe(false); + expect(isJSONObject(123)).toBe(false); + expect(isJSONObject(true)).toBe(false); + expect(isJSONObject([1, 2, 3])).toBe(false); + expect(isJSONObject({ date: new Date() })).toBe(false); + expect(isJSONObject({ func: () => {} })).toBe(false); + expect(isJSONObject({ regexp: /test/ })).toBe(false); + expect(isJSONObject({ undefinedProperty: undefined })).toBe(false); + expect(isJSONObject({ symbolProperty: Symbol('test') })).toBe(false); + expect(isJSONObject({ nested: { a: function* () {} } })).toBe(false); + }); + + it('isJSONObject should return false when key is not a string', () => { + expect(isJSONObject({ [Symbol('a')]: 'a' })).toBe(false); + }); +}); diff --git a/src/predicate/isJSONValue.ts b/src/predicate/isJSONValue.ts index 8f6ca4a9..4cc274fa 100644 --- a/src/predicate/isJSONValue.ts +++ b/src/predicate/isJSONValue.ts @@ -1,5 +1,8 @@ -import { isJSONArray } from './isJSONArray.ts'; -import { isJSONObject } from './isJSONObject.ts'; +/** + * The functions isJSONValue, isJSONArray, and isJSONObject are grouped in this file + * to prevent any circular dependency issues. + */ +import { isPlainObject } from './isPlainObject.ts'; /** * Checks if a given value is a valid JSON value. @@ -40,3 +43,61 @@ export function isJSONValue(value: unknown): value is Record | any[ } } } + +/** + * Checks if a given value is a valid JSON array. + * + * A valid JSON array is defined as an array where all items are valid JSON values. + * + * @param {unknown} value - The value to check. + * @returns {value is any[]} - True if the value is a valid JSON array, otherwise false. + * + * @example + * console.log(isJSONArray([1, 2, 3])); // true + * console.log(isJSONArray(["string", null, true])); // true + * console.log(isJSONArray([1, 2, () => {}])); // false + * console.log(isJSONArray("not an array")); // false + */ +export function isJSONArray(value: unknown): value is any[] { + if (!Array.isArray(value)) { + return false; + } + + return value.every(item => isJSONValue(item)); +} + +/** + * Checks if a value is a JSON object. + * + * A valid JSON object is defined as an object with string keys and valid JSON values. + * + * @param {unknown} obj The value to check. + * @returns {obj is Record} True if `obj` is a JSON object, false otherwise. + * + * @example + * isJSONObject({ nested: { boolean: true, array: [1, 2, 3], string: 'test', null: null } }); // true + * isJSONObject({ regexp: /test/ }); // false + * isJSONObject(123); // false + */ +export function isJSONObject(obj: unknown): obj is Record { + if (!isPlainObject(obj)) { + return false; + } + + const keys = Reflect.ownKeys(obj); + + for (let i = 0; i < keys.length; i++) { + const key = keys[i]; + const value = obj[key]; + + if (typeof key !== 'string') { + return false; + } + + if (!isJSONValue(value)) { + return false; + } + } + + return true; +}