From ded56755f334a6ee82a524ee588678bb35912721 Mon Sep 17 00:00:00 2001 From: knott <153616637+knott11@users.noreply.github.com> Date: Sun, 29 Sep 2024 19:42:50 +0800 Subject: [PATCH] feat(isJSONObject, isJSONValue, isJSONArray): Implement `isJSONObject`, `isJSONValue`, `isJSONArray` (#563) * feat:init * feat * fix:use prettier * feat(isPlainObject): Update signature of isPlainObject * feat: Add isJSONValue, isJSONArray, and refactor isJSONObject * remove from functions * docs: Update docs * docs: Update docs * docs: Update docs * docs: Update docs * docs: Update docs * docs: Update docs * docs: Update docs --------- Co-authored-by: Sojin Park Co-authored-by: Sojin Park --- benchmarks/performance/isJSONObject.bench.ts | 12 ++++++ docs/.vitepress/libs/functions.json | 12 ++++-- docs/ja/reference/predicate/isJSONArray.md | 28 +++++++++++++ docs/ja/reference/predicate/isJSONObject.md | 27 ++++++++++++ docs/ja/reference/predicate/isJSONValue.md | 39 +++++++++++++++++ docs/ja/reference/predicate/isPlainObject.md | 6 +-- docs/ko/reference/predicate/isJSONArray.md | 28 +++++++++++++ docs/ko/reference/predicate/isJSONObject.md | 27 ++++++++++++ docs/ko/reference/predicate/isJSONValue.md | 39 +++++++++++++++++ docs/ko/reference/predicate/isPlainObject.md | 6 +-- docs/reference/predicate/isJSONArray.md | 28 +++++++++++++ docs/reference/predicate/isJSONObject.md | 28 +++++++++++++ docs/reference/predicate/isJSONValue.md | 39 +++++++++++++++++ docs/reference/predicate/isPlainObject.md | 6 +-- .../reference/predicate/isJSONArray.md | 28 +++++++++++++ .../reference/predicate/isJSONObject.md | 28 +++++++++++++ .../reference/predicate/isJSONValue.md | 39 +++++++++++++++++ .../reference/predicate/isPlainObject.md | 6 +-- src/predicate/index.ts | 3 ++ src/predicate/isJSONArray.spec.ts | 21 ++++++++++ src/predicate/isJSONArray.ts | 23 ++++++++++ src/predicate/isJSONObject.spec.ts | 25 +++++++++++ src/predicate/isJSONObject.ts | 38 +++++++++++++++++ src/predicate/isJSONValue.spec.ts | 36 ++++++++++++++++ src/predicate/isJSONValue.ts | 42 +++++++++++++++++++ src/predicate/isPlainObject.ts | 18 ++++---- 26 files changed, 608 insertions(+), 24 deletions(-) create mode 100644 benchmarks/performance/isJSONObject.bench.ts create mode 100644 docs/ja/reference/predicate/isJSONArray.md create mode 100644 docs/ja/reference/predicate/isJSONObject.md create mode 100644 docs/ja/reference/predicate/isJSONValue.md create mode 100644 docs/ko/reference/predicate/isJSONArray.md create mode 100644 docs/ko/reference/predicate/isJSONObject.md create mode 100644 docs/ko/reference/predicate/isJSONValue.md create mode 100644 docs/reference/predicate/isJSONArray.md create mode 100644 docs/reference/predicate/isJSONObject.md create mode 100644 docs/reference/predicate/isJSONValue.md create mode 100644 docs/zh_hans/reference/predicate/isJSONArray.md create mode 100644 docs/zh_hans/reference/predicate/isJSONObject.md create mode 100644 docs/zh_hans/reference/predicate/isJSONValue.md create mode 100644 src/predicate/isJSONArray.spec.ts create mode 100644 src/predicate/isJSONArray.ts create mode 100644 src/predicate/isJSONObject.spec.ts create mode 100644 src/predicate/isJSONObject.ts create mode 100644 src/predicate/isJSONValue.spec.ts create mode 100644 src/predicate/isJSONValue.ts diff --git a/benchmarks/performance/isJSONObject.bench.ts b/benchmarks/performance/isJSONObject.bench.ts new file mode 100644 index 00000000..a3b84440 --- /dev/null +++ b/benchmarks/performance/isJSONObject.bench.ts @@ -0,0 +1,12 @@ +import { bench, describe } from 'vitest'; +import { isJSONObject as isJSONObjectToolkit } from 'es-toolkit'; + +describe('isJSONObject', () => { + bench('es-toolkit/isJSONObject', () => { + isJSONObjectToolkit({ nested: { boolean: true, array: [1, 2, 3], string: 'test', null: null } }); + isJSONObjectToolkit({ date: new Date() }); + isJSONObjectToolkit({ nested: { a: function* () {} } }); + isJSONObjectToolkit(undefined); + isJSONObjectToolkit(/test/); + }); +}); diff --git a/docs/.vitepress/libs/functions.json b/docs/.vitepress/libs/functions.json index 3aaa9711..9add8d8e 100644 --- a/docs/.vitepress/libs/functions.json +++ b/docs/.vitepress/libs/functions.json @@ -93,7 +93,9 @@ "some", "sortBy" ], - "date": ["now"], + "date": [ + "now" + ], "function": [ "after", "ary", @@ -194,7 +196,11 @@ "sum", "sumBy" ], - "number": ["clamp", "inRange", "random"], + "number": [ + "clamp", + "inRange", + "random" + ], "object": [ "assign", "assignIn", @@ -308,4 +314,4 @@ "toPath", "uniqueId" ] -} +} \ No newline at end of file diff --git a/docs/ja/reference/predicate/isJSONArray.md b/docs/ja/reference/predicate/isJSONArray.md new file mode 100644 index 00000000..164704b6 --- /dev/null +++ b/docs/ja/reference/predicate/isJSONArray.md @@ -0,0 +1,28 @@ +# isJSONArray + +与えられた値が有効なJSON配列かどうかを確認します。 + +有効なJSON配列は、すべての項目が[有効なJSON値](./isJSONValue.md)である配列として定義されます。 + +## インターフェース + +```typescript +function isJSONArray(value: unknown): value is any[]; +``` + +### パラメータ + +- `value` (`unknown`): 確認する値。 + +### 戻り値 + +(`value is any[]`): 値が有効なJSON配列であれば`true`、それ以外の場合は`false`。 + +## 例 + +```typescript +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 +``` diff --git a/docs/ja/reference/predicate/isJSONObject.md b/docs/ja/reference/predicate/isJSONObject.md new file mode 100644 index 00000000..a3dae0fe --- /dev/null +++ b/docs/ja/reference/predicate/isJSONObject.md @@ -0,0 +1,27 @@ +# isJSONObject + +値がJSONオブジェクトかどうかを確認します。 + +有効なJSONオブジェクトとは、キーが文字列で、値が[有効なJSON値](./isJSONValue.md)を持つオブジェクトです。 + +## インターフェース + +```typescript +function isJSONObject(obj: unknown): obj is Record; +``` + +### パラメータ + +- `obj` (`unknown`): 確認する値。 + +### 戻り値 + +(`obj is Record`): `obj`がJSONオブジェクトである場合は`true`、それ以外の場合は`false`。 + +## 例 + +```typescript +isJSONObject({ nested: { boolean: true, array: [1, 2, 3], string: 'test', null: null } }); // true +isJSONObject({ regexp: /test/ }); // false +isJSONObject(123); // false +``` diff --git a/docs/ja/reference/predicate/isJSONValue.md b/docs/ja/reference/predicate/isJSONValue.md new file mode 100644 index 00000000..e9fe3ae7 --- /dev/null +++ b/docs/ja/reference/predicate/isJSONValue.md @@ -0,0 +1,39 @@ +# isJSONValue + +与えられた値が有効なJSON値かどうかを確認します。 + +有効なJSON値とは、次のいずれかを指します。 + +- [JSONオブジェクト](./isJSONObject.md)(文字列キーと有効なJSON値を持つオブジェクト) +- [JSON配列](./isJSONArray.md)(有効なJSON値の配列) +- 文字列(`string`) +- 数字(`number`) +- 真偽値(`boolean`) +- `null` + +## インターフェース + +```typescript +function isJSONValue(value: unknown): value is Record | any[] | string | number | boolean | null; +``` + +### パラメータ + +- `value` (`unknown`): チェックする値。 + +### 戻り値 + +(`value is Record | any[] | string | number | boolean | null`): 値が有効なJSON値であれば`true`、そうでなければ`false`。 + +## 例 + +```typescript +console.log(isJSONValue(null)); // true +console.log(isJSONValue({ key: 'value' })); // true +console.log(isJSONValue([1, 2, 3])); // true +console.log(isJSONValue('Hello')); // true +console.log(isJSONValue(42)); // true +console.log(isJSONValue(true)); // true +console.log(isJSONValue(undefined)); // false +console.log(isJSONValue(() => {})); // false +``` diff --git a/docs/ja/reference/predicate/isPlainObject.md b/docs/ja/reference/predicate/isPlainObject.md index d8cd44c6..04f69a47 100644 --- a/docs/ja/reference/predicate/isPlainObject.md +++ b/docs/ja/reference/predicate/isPlainObject.md @@ -5,16 +5,16 @@ ## インターフェース ```typescript -function isPlainObject(object: object): boolean; +function isPlainObject(value: unknown): value is Record; ``` ### パラメータ -- `object` (`object`): 検査する値。 +- `value` (`unknown`): 検査する値。 ### 戻り値 -(`boolean`): 値がプレーンオブジェクトの場合はtrue。 +(`value is Record`): 値がプレーンオブジェクトの場合は`true`。 ## 例 diff --git a/docs/ko/reference/predicate/isJSONArray.md b/docs/ko/reference/predicate/isJSONArray.md new file mode 100644 index 00000000..a683be91 --- /dev/null +++ b/docs/ko/reference/predicate/isJSONArray.md @@ -0,0 +1,28 @@ +# isJSONArray + +주어진 값이 유효한 JSON 배열인지 확인해요. + +유효한 JSON 배열이란, 모든 항목이 [유효한 JSON 값](./isJSONValue.md)인 배열이에요. + +## 인터페이스 + +```typescript +function isJSONArray(value: unknown): value is any[]; +``` + +### 파라미터 + +- `value` (`unknown`): 확인할 값. + +### 반환 값 + +(`value is any[]`): 값이 유효한 JSON 배열이면 `true`, 그렇지 않으면 `false`. + +## 예시 + +```typescript +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 +``` diff --git a/docs/ko/reference/predicate/isJSONObject.md b/docs/ko/reference/predicate/isJSONObject.md new file mode 100644 index 00000000..dc352507 --- /dev/null +++ b/docs/ko/reference/predicate/isJSONObject.md @@ -0,0 +1,27 @@ +# isJSONObject + +값이 JSON 객체인지 확인해요. + +유효한 JSON 객체란, 키로 문자열을 가지고, 값으로 [유효한 JSON 값](./isJSONValue.md)을 가진 객체예요. + +## 인터페이스 + +```typescript +function isJSONObject(obj: unknown): obj is Record; +``` + +### 파라미터 + +- `obj` (`unknown`): 확인할 값. + +### 반환 값 + +(`obj is Record`): `obj`가 JSON 객체이면 `true`, 그렇지 않으면 `false`. + +## 예시 + +```typescript +isJSONObject({ nested: { boolean: true, array: [1, 2, 3], string: 'test', null: null } }); // true +isJSONObject({ regexp: /test/ }); // false +isJSONObject(123); // false +``` diff --git a/docs/ko/reference/predicate/isJSONValue.md b/docs/ko/reference/predicate/isJSONValue.md new file mode 100644 index 00000000..42318199 --- /dev/null +++ b/docs/ko/reference/predicate/isJSONValue.md @@ -0,0 +1,39 @@ +# isJSONValue + +주어진 값이 유효한 JSON 값인지 확인해요. + +유효한 JSON 값이란, 다음 중 하나를 말해요. + +- [JSON 객체](./isJSONObject.md) (문자열 키와 유효한 JSON 값을 가진 객체) +- [JSON 배열](./isJSONArray.md) (유효한 JSON 값의 배열) +- 문자열 (`string`) +- 숫자 (`number`) +- 참값 (`boolean`) +- `null` + +## 인터페이스 + +```typescript +function isJSONValue(value: unknown): value is Record | any[] | string | number | boolean | null; +``` + +### 파라미터 + +- `value` (`unknown`): 확인할 값. + +### 반환 값 + +(`value is Record | any[] | string | number | boolean | null`): 값이 유효한 JSON 값이면 `true`, 그렇지 않으면 `false`. + +## 예시 + +```typescript +console.log(isJSONValue(null)); // true +console.log(isJSONValue({ key: 'value' })); // true +console.log(isJSONValue([1, 2, 3])); // true +console.log(isJSONValue('Hello')); // true +console.log(isJSONValue(42)); // true +console.log(isJSONValue(true)); // true +console.log(isJSONValue(undefined)); // false +console.log(isJSONValue(() => {})); // false +``` diff --git a/docs/ko/reference/predicate/isPlainObject.md b/docs/ko/reference/predicate/isPlainObject.md index 0d3d00c6..7c165c85 100644 --- a/docs/ko/reference/predicate/isPlainObject.md +++ b/docs/ko/reference/predicate/isPlainObject.md @@ -5,16 +5,16 @@ ## 인터페이스 ```typescript -function isPlainObject(object: object): boolean; +function isPlainObject(value: unknown): value is Record; ``` ### 파라미터 -- `object` (`object`): 검사할 값. +- `value` (`unknown`): 검사할 값. ### 반환 값 -(`boolean`): 값이 순수 객체이면 true. +(`value is Record`): 값이 순수 객체이면 `true`. ## 예시 diff --git a/docs/reference/predicate/isJSONArray.md b/docs/reference/predicate/isJSONArray.md new file mode 100644 index 00000000..b43fd7c9 --- /dev/null +++ b/docs/reference/predicate/isJSONArray.md @@ -0,0 +1,28 @@ +# isJSONArray + +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](./isJSONValue.md). + +## Signature + +```typescript +function isJSONArray(value: unknown): value is any[]; +``` + +### Parameters + +- `value` (`unknown`): The value to check. + +### Returns + +(`value is any[]`): True if the value is a valid JSON array, otherwise false. + +## Examples + +```typescript +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 +``` diff --git a/docs/reference/predicate/isJSONObject.md b/docs/reference/predicate/isJSONObject.md new file mode 100644 index 00000000..912c4970 --- /dev/null +++ b/docs/reference/predicate/isJSONObject.md @@ -0,0 +1,28 @@ +# isJSONObject + +Checks if a value is a JSON object. + +A valid JSON object is defined as an object with string keys and valid [JSON values](./isJSONValue.md). + +## Signature + +```typescript +function isJSONObject(obj: unknown): obj is Record; +``` + +### Parameters + +- `value` (`unknown`): The value to check. + +### Returns + +(`obj is Record`): True if the value is a JSON object, otherwise false. + +## Examples + +```typescript +console.log(isJSONObject({ nested: { boolean: true, array: [1, 2, 3], string: 'test', number: 1, null: null } })); // true +console.log(isJSONObject({})); // true +console.log(isJSONObject({ regexp: /test/ })); // false +console.log(isJSONObject(123)); // false +``` diff --git a/docs/reference/predicate/isJSONValue.md b/docs/reference/predicate/isJSONValue.md new file mode 100644 index 00000000..16508712 --- /dev/null +++ b/docs/reference/predicate/isJSONValue.md @@ -0,0 +1,39 @@ +# isJSONValue + +Checks if a given value is a valid JSON value. + +A valid JSON value can be: + +- [a JSON object](./isJSONObject.md) (an object with string keys and valid JSON values) +- [a JSON array](./isJSONArray.md) (an array of valid JSON values) +- a `string` +- a `number` +- a `boolean` +- `null` + +## Signature + +```typescript +function isJSONValue(value: unknown): value is Record | any[] | string | number | boolean | null; +``` + +### Parameters + +- `value` (`unknown`): The value to check. + +### Returns + +(`value is Record | any[] | string | number | boolean | null`): True if the value is a valid JSON value, otherwise false. + +## Examples + +```typescript +console.log(isJSONValue(null)); // true +console.log(isJSONValue({ key: 'value' })); // true +console.log(isJSONValue([1, 2, 3])); // true +console.log(isJSONValue('Hello')); // true +console.log(isJSONValue(42)); // true +console.log(isJSONValue(true)); // true +console.log(isJSONValue(undefined)); // false +console.log(isJSONValue(() => {})); // false +``` diff --git a/docs/reference/predicate/isPlainObject.md b/docs/reference/predicate/isPlainObject.md index 31dafa17..28048d33 100644 --- a/docs/reference/predicate/isPlainObject.md +++ b/docs/reference/predicate/isPlainObject.md @@ -5,16 +5,16 @@ Checks if a given value is a plain object. ## Signature ```typescript -function isPlainObject(object: object): boolean; +function isPlainObject(value: unknown): value is Record; ``` ### Parameters -- `object` (`object`): The value to check. +- `value` (`unknown`): The value to check. ### Returns -(`boolean`): True if the value is a plain object, otherwise false. +(`value is Record`): True if the value is a plain object, otherwise false. ## Examples diff --git a/docs/zh_hans/reference/predicate/isJSONArray.md b/docs/zh_hans/reference/predicate/isJSONArray.md new file mode 100644 index 00000000..88e5523e --- /dev/null +++ b/docs/zh_hans/reference/predicate/isJSONArray.md @@ -0,0 +1,28 @@ +# isJSONArray + +检查给定值是否是有效的 JSON 数组。 + +有效的 JSON 数组定义为所有项都是[有效 JSON 值](./isJSONValue.md)的数组。 + +## 签名 + +```typescript +function isJSONArray(value: unknown): value is any[]; +``` + +### 参数 + +- `value` (`unknown`): 要检查的值。 + +### 返回值 + +(`value is any[]`): 如果该值是有效的 JSON 数组则为 `true`,否则为 `false`。 + +## 示例 + +```typescript +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 +``` diff --git a/docs/zh_hans/reference/predicate/isJSONObject.md b/docs/zh_hans/reference/predicate/isJSONObject.md new file mode 100644 index 00000000..b857d83d --- /dev/null +++ b/docs/zh_hans/reference/predicate/isJSONObject.md @@ -0,0 +1,28 @@ +# isJSONObject + +检查一个值是否是合法的JSON对象。 + +一个有效的JSON对象是一个具有字符串键和[有效JSON值](./isJSONValue.md)的对象。 + +## 签名 + +```typescript +function isJSONObject(value: unknown): boolean; +``` + +### 参数 + +- `value` (`unknown`): 要检查的值。 + +### 返回值 + +(`boolean`): 如果该值是JSON对象,则返回 `true`,否则返回 `false`。 + +## 示例 + +```typescript +console.log(isJSONObject({ nested: { boolean: true, array: [1, 2, 3], string: 'test', null: null } })); // true +console.log(isJSONObject({})); // true +console.log(isJSONObject({ regexp: /test/ })); // false +console.log(isJSONObject(123)); // false +``` diff --git a/docs/zh_hans/reference/predicate/isJSONValue.md b/docs/zh_hans/reference/predicate/isJSONValue.md new file mode 100644 index 00000000..771adc2e --- /dev/null +++ b/docs/zh_hans/reference/predicate/isJSONValue.md @@ -0,0 +1,39 @@ +# isJSONValue + +检查给定值是否为有效的JSON值。 + +一个有效的JSON值可以是: + +- [一个JSON对象](./isJSONObject.md) (一个具有字符串键和有效JSON值的对象) +- [一个JSON数组](./isJSONArray.md) (一个有效JSON值的数组) +- 字符串 (`string`) +- 数字 (`number`) +- 布尔值 (`boolean`) +- `null` + +## 签名 + +```typescript +function isJSONValue(value: unknown): value is Record | any[] | string | number | boolean | null; +``` + +### 参数 + +- `value` (`unknown`): 要检查的值。 + +### 返回值 + +(`value is Record | any[] | string | number | boolean | null`): 如果值是有效的JSON值,则为 `true`,否则为 `false`。 + +## 示例 + +```typescript +console.log(isJSONValue(null)); // true +console.log(isJSONValue({ key: 'value' })); // true +console.log(isJSONValue([1, 2, 3])); // true +console.log(isJSONValue('Hello')); // true +console.log(isJSONValue(42)); // true +console.log(isJSONValue(true)); // true +console.log(isJSONValue(undefined)); // false +console.log(isJSONValue(() => {})); // false +``` diff --git a/docs/zh_hans/reference/predicate/isPlainObject.md b/docs/zh_hans/reference/predicate/isPlainObject.md index a5e6f9a2..1a40909e 100644 --- a/docs/zh_hans/reference/predicate/isPlainObject.md +++ b/docs/zh_hans/reference/predicate/isPlainObject.md @@ -5,16 +5,16 @@ ## 签名 ```typescript -function isPlainObject(object: object): boolean; +function isPlainObject(value: unknown): value is Record; ``` ### 参数 -- `object` (`object`): 要检查的值。 +- `value` (`unknown`): 要检查的值。 ### 返回值 -(`boolean`): 如果该值是普通对象,则返回 `true`,否则返回 `false`。 +(`value is Record`): 如果该值是普通对象,则返回 `true`,否则返回 `false`。 ## 示例 diff --git a/src/predicate/index.ts b/src/predicate/index.ts index b5f0e0d7..2c6bb496 100644 --- a/src/predicate/index.ts +++ b/src/predicate/index.ts @@ -15,6 +15,9 @@ export { isRegExp } from './isRegExp.ts'; export { isBoolean } from './isBoolean.ts'; export { isSymbol } from './isSymbol.ts'; export { isString } from './isString.ts'; +export { isJSONObject } from './isJSONObject.ts'; +export { isJSONValue } from './isJSONValue.ts'; +export { isJSONArray } from './isJSONArray.ts'; export { isSet } from './isSet.ts'; export { isWeakMap } from './isWeakMap.ts'; export { isWeakSet } from './isWeakSet.ts'; diff --git a/src/predicate/isJSONArray.spec.ts b/src/predicate/isJSONArray.spec.ts new file mode 100644 index 00000000..e30be654 --- /dev/null +++ b/src/predicate/isJSONArray.spec.ts @@ -0,0 +1,21 @@ +import { isJSONArray } from './isJSONArray.ts'; +import { describe, expect, it } from 'vitest'; + +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 new file mode 100644 index 00000000..2a4bfd32 --- /dev/null +++ b/src/predicate/isJSONArray.ts @@ -0,0 +1,23 @@ +import { isJSONValue } from './isJSONValue'; + +/** + * 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 new file mode 100644 index 00000000..aef24b4b --- /dev/null +++ b/src/predicate/isJSONObject.spec.ts @@ -0,0 +1,25 @@ +import { isJSONObject } from './isJSONObject.ts'; +import { describe, it, expect } from 'vitest'; + +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); + }); +}); diff --git a/src/predicate/isJSONObject.ts b/src/predicate/isJSONObject.ts new file mode 100644 index 00000000..1089b2ec --- /dev/null +++ b/src/predicate/isJSONObject.ts @@ -0,0 +1,38 @@ +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 new file mode 100644 index 00000000..0e3186fc --- /dev/null +++ b/src/predicate/isJSONValue.spec.ts @@ -0,0 +1,36 @@ +import { describe, expect, it } from 'vitest'; +import { isJSONValue } from './isJSONValue.ts'; + +describe('isJSONValue', () => { + it('should return true for null', () => { + expect(isJSONValue(null)).toBe(true); + }); + + it('should return true for a valid JSON object', () => { + expect(isJSONValue({ key: 'value' })).toBe(true); + expect( + isJSONValue({ + nested: { boolean: true, array: [1, 2, 3], string: 'test', null: null }, + }) + ).toBe(true); + }); + + it('should return true for a valid JSON array', () => { + expect(isJSONValue([1, 2, 3])).toBe(true); + expect(isJSONValue(['string', 42, null, true])).toBe(true); + }); + + it('should return true for valid JSON primitive types', () => { + expect(isJSONValue('Hello')).toBe(true); + expect(isJSONValue(42)).toBe(true); + expect(isJSONValue(true)).toBe(true); + }); + + it('should return false for invalid JSON values', () => { + expect(isJSONValue(undefined)).toBe(false); + expect(isJSONValue(() => {})).toBe(false); + expect(isJSONValue(new Date())).toBe(false); + expect(isJSONValue(/regex/)).toBe(false); + expect(isJSONValue(Symbol('symbol'))).toBe(false); + }); +}); diff --git a/src/predicate/isJSONValue.ts b/src/predicate/isJSONValue.ts new file mode 100644 index 00000000..8f6ca4a9 --- /dev/null +++ b/src/predicate/isJSONValue.ts @@ -0,0 +1,42 @@ +import { isJSONArray } from './isJSONArray.ts'; +import { isJSONObject } from './isJSONObject.ts'; + +/** + * Checks if a given value is a valid JSON value. + * + * A valid JSON value can be: + * - null + * - a JSON object (an object with string keys and valid JSON values) + * - a JSON array (an array of valid JSON values) + * - a string + * - a number + * - a boolean + * + * @param {unknown} value - The value to check. + * @returns {boolean} - True if the value is a valid JSON value, otherwise false. + * + * @example + * console.log(isJSONValue(null)); // true + * console.log(isJSONValue({ key: "value" })); // true + * console.log(isJSONValue([1, 2, 3])); // true + * console.log(isJSONValue("Hello")); // true + * console.log(isJSONValue(42)); // true + * console.log(isJSONValue(true)); // true + * console.log(isJSONValue(undefined)); // false + * console.log(isJSONValue(() => {})); // false + */ +export function isJSONValue(value: unknown): value is Record | any[] | string | number | boolean | null { + switch (typeof value) { + case 'object': { + return value === null || isJSONArray(value) || isJSONObject(value); + } + case 'string': + case 'number': + case 'boolean': { + return true; + } + default: { + return false; + } + } +} diff --git a/src/predicate/isPlainObject.ts b/src/predicate/isPlainObject.ts index 52b45474..b3409312 100644 --- a/src/predicate/isPlainObject.ts +++ b/src/predicate/isPlainObject.ts @@ -1,8 +1,8 @@ /** * Checks if a given value is a plain object. * - * @param {object} object - The value to check. - * @returns {boolean} - True if the value is a plain object, otherwise false. + * @param {object} value - The value to check. + * @returns {value is Record} - True if the value is a plain object, otherwise false. * * @example * console.log(isPlainObject({})); // true @@ -11,28 +11,28 @@ * console.log(isPlainObject(Object.create(null))); // true * console.log(Buffer.from('hello, world')); // false */ -export function isPlainObject(object: object): boolean { - if (typeof object !== 'object') { +export function isPlainObject(value: unknown): value is Record { + if (typeof value !== 'object') { return false; } - if (object == null) { + if (value == null) { return false; } - if (Object.getPrototypeOf(object) === null) { + if (Object.getPrototypeOf(value) === null) { return true; } - if (object.toString() !== '[object Object]') { + if (value.toString() !== '[object Object]') { return false; } - let proto = object; + let proto = value; while (Object.getPrototypeOf(proto) !== null) { proto = Object.getPrototypeOf(proto); } - return Object.getPrototypeOf(object) === proto; + return Object.getPrototypeOf(value) === proto; }