From caedf69aedd29dc9888491420eebccc540db9654 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EB=B3=91=EC=A7=84?= <64676070+sunrabbit123@users.noreply.github.com> Date: Fri, 19 Jul 2024 19:18:20 -0700 Subject: [PATCH] feat(isLength): Add `isLength` (#245) * feat(predicate, compat, isLength): impl isLength Signed-off-by: sunrabbit123 * docs: change compatibility mark Signed-off-by: sunrabbit123 * test(isLength, compat): add test case link Signed-off-by: sunrabbit123 * docs(predicate, isLength): add route path on vitepress Signed-off-by: sunrabbit123 * Update docs/ko/reference/predicate/isLength.md --------- Signed-off-by: sunrabbit123 Co-authored-by: Sojin Park Co-authored-by: Sojin Park --- benchmarks/performance/isLength.bench.ts | 34 ++++++++++++++++++++ docs/.vitepress/en.mts | 1 + docs/.vitepress/ko.mts | 1 + docs/compatibility.md | 2 +- docs/ko/compatibility.md | 2 +- docs/ko/reference/predicate/isLength.md | 41 ++++++++++++++++++++++++ docs/reference/predicate/isLength.md | 41 ++++++++++++++++++++++++ src/compat/predicate/isLength.spec.ts | 23 +++++++++++++ src/predicate/index.ts | 3 +- src/predicate/isLength.spec.ts | 20 ++++++++++++ src/predicate/isLength.ts | 3 ++ 11 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 benchmarks/performance/isLength.bench.ts create mode 100644 docs/ko/reference/predicate/isLength.md create mode 100644 docs/reference/predicate/isLength.md create mode 100644 src/compat/predicate/isLength.spec.ts create mode 100644 src/predicate/isLength.spec.ts create mode 100644 src/predicate/isLength.ts diff --git a/benchmarks/performance/isLength.bench.ts b/benchmarks/performance/isLength.bench.ts new file mode 100644 index 00000000..bcb9d17f --- /dev/null +++ b/benchmarks/performance/isLength.bench.ts @@ -0,0 +1,34 @@ +import { bench, describe } from 'vitest'; +import { isLength as isLengthToolkit } from 'es-toolkit'; +import { isLength as isLengthLodash } from 'lodash'; + +describe('isLength', () => { + bench('es-toolkit/isLength', () => { + isLengthToolkit(100); + isLengthToolkit(0); + isLengthToolkit(-1); + isLengthToolkit(1.5); + isLengthToolkit(Number.MAX_SAFE_INTEGER); + isLengthToolkit(Number.MAX_SAFE_INTEGER + 1); + isLengthToolkit('100'); + isLengthToolkit(true); + isLengthToolkit(null); + isLengthToolkit(undefined); + isLengthToolkit({}); + isLengthToolkit([]); + }); + bench('lodash/isLength', () => { + isLengthLodash(100); + isLengthLodash(0); + isLengthLodash(-1); + isLengthLodash(1.5); + isLengthLodash(Number.MAX_SAFE_INTEGER); + isLengthLodash(Number.MAX_SAFE_INTEGER + 1); + isLengthLodash('100'); + isLengthLodash(true); + isLengthLodash(null); + isLengthLodash(undefined); + isLengthLodash({}); + isLengthLodash([]); + }); +}); diff --git a/docs/.vitepress/en.mts b/docs/.vitepress/en.mts index 1d2b614b..6d106e66 100644 --- a/docs/.vitepress/en.mts +++ b/docs/.vitepress/en.mts @@ -151,6 +151,7 @@ function sidebar(): DefaultTheme.Sidebar { text: 'Predicates', items: [ { text: 'isEqual', link: '/reference/predicate/isEqual' }, + { text: 'isLength', link: '/reference/predicate/isLength' }, { text: 'isPlainObject', link: '/reference/predicate/isPlainObject' }, { text: 'isNil', link: '/reference/predicate/isNil' }, { text: 'isNotNil', link: '/reference/predicate/isNotNil' }, diff --git a/docs/.vitepress/ko.mts b/docs/.vitepress/ko.mts index f9e43955..cc79f053 100644 --- a/docs/.vitepress/ko.mts +++ b/docs/.vitepress/ko.mts @@ -162,6 +162,7 @@ function sidebar(): DefaultTheme.Sidebar { text: '타입 가드', items: [ { text: 'isEqual', link: '/ko/reference/predicate/isEqual' }, + { text: 'isLength', link: '/ko/reference/predicate/isLength' }, { text: 'isPlainObject', link: '/ko/reference/predicate/isPlainObject' }, { text: 'isNil', link: '/ko/reference/predicate/isNil' }, { text: 'isNotNil', link: '/ko/reference/predicate/isNotNil' }, diff --git a/docs/compatibility.md b/docs/compatibility.md index 3a2439d8..e4d1cbb5 100644 --- a/docs/compatibility.md +++ b/docs/compatibility.md @@ -209,7 +209,7 @@ Even if a feature is marked "in review," it might already be under review to ens | [isFinite](https://lodash.com/docs/4.17.15#isFinite) | ❌ | | [isFunction](https://lodash.com/docs/4.17.15#isFunction) | ❌ | | [isInteger](https://lodash.com/docs/4.17.15#isInteger) | ❌ | -| [isLength](https://lodash.com/docs/4.17.15#isLength) | ❌ | +| [isLength](https://lodash.com/docs/4.17.15#isLength) | ✅ | | [isMap](https://lodash.com/docs/4.17.15#isMap) | ❌ | | [isMatch](https://lodash.com/docs/4.17.15#isMatch) | ❌ | | [isMatchWith](https://lodash.com/docs/4.17.15#isMatchWith) | ❌ | diff --git a/docs/ko/compatibility.md b/docs/ko/compatibility.md index 685db94c..6f3aa1ef 100644 --- a/docs/ko/compatibility.md +++ b/docs/ko/compatibility.md @@ -210,7 +210,7 @@ chunk([1, 2, 3, 4], 0); | [isFinite](https://lodash.com/docs/4.17.15#isFinite) | ❌ | | [isFunction](https://lodash.com/docs/4.17.15#isFunction) | ❌ | | [isInteger](https://lodash.com/docs/4.17.15#isInteger) | ❌ | -| [isLength](https://lodash.com/docs/4.17.15#isLength) | ❌ | +| [isLength](https://lodash.com/docs/4.17.15#isLength) | ✅ | | [isMap](https://lodash.com/docs/4.17.15#isMap) | ❌ | | [isMatch](https://lodash.com/docs/4.17.15#isMatch) | ❌ | | [isMatchWith](https://lodash.com/docs/4.17.15#isMatchWith) | ❌ | diff --git a/docs/ko/reference/predicate/isLength.md b/docs/ko/reference/predicate/isLength.md new file mode 100644 index 00000000..df4e50ec --- /dev/null +++ b/docs/ko/reference/predicate/isLength.md @@ -0,0 +1,41 @@ +# isLength + +주어진 값이 유효한 길이인지 확인해요. + +유효한 길이란, `0` 이상 `Number.MAX_SAFE_INTEGER` 미만의 정수를 말해요. + +TypeScript의 타입 가드로 사용할 수 있어요. 파라미터로 주어진 값의 타입을 `number`로 좁혀요. + +## Signature + +```typescript +function isLength(value: unknown): value is number; +``` + +### Parameters + +- `value` (`unknown`): 유효한 길이인지 확인할 값 + +### Returns + +(`value is number`): 값이 유효한 길이면 `true`, 아니면 `false`. + +## Examples + +```typescript +import { isLength } from 'es-toolkit/predicate'; + +const value1 = 0; +const value2 = 42; +const value3 = -1; +const value4 = 1.5; +const value5 = Number.MAX_SAFE_INTEGER; +const value6 = Number.MAX_SAFE_INTEGER + 1; + +console.log(isLength(value1)); // true +console.log(isLength(value2)); // true +console.log(isLength(value3)); // false +console.log(isLength(value4)); // false +console.log(isLength(value5)); // true +console.log(isLength(value6)); // false +``` diff --git a/docs/reference/predicate/isLength.md b/docs/reference/predicate/isLength.md new file mode 100644 index 00000000..e9279600 --- /dev/null +++ b/docs/reference/predicate/isLength.md @@ -0,0 +1,41 @@ +# isLength + +Checks if a given value is a valid length. + +This function tests whether the provided value is of type `number`, is a non-negative integer, and is less than or equal to JavaScript's maximum safe integer (`Number.MAX_SAFE_INTEGER`). It returns `true` if the value is a valid length, and `false` otherwise. + +This function can also serve as a type predicate in TypeScript, narrowing the type of the argument to a valid length (`number`). + +## Signature + +```typescript +function isLength(value: unknown): value is number; +``` + +### Parameters + +- `value` (`unknown`): The value to check if it is a valid length. + +### Returns + +(`value is number`): Returns `true` if the value is a valid length, otherwise `false`. + +## Examples + +```typescript +import { isLength } from 'es-toolkit/predicate'; + +const value1 = 0; +const value2 = 42; +const value3 = -1; +const value4 = 1.5; +const value5 = Number.MAX_SAFE_INTEGER; +const value6 = Number.MAX_SAFE_INTEGER + 1; + +console.log(isLength(value1)); // true +console.log(isLength(value2)); // true +console.log(isLength(value3)); // false +console.log(isLength(value4)); // false +console.log(isLength(value5)); // true +console.log(isLength(value6)); // false +``` diff --git a/src/compat/predicate/isLength.spec.ts b/src/compat/predicate/isLength.spec.ts new file mode 100644 index 00000000..378502a6 --- /dev/null +++ b/src/compat/predicate/isLength.spec.ts @@ -0,0 +1,23 @@ +import { isLength } from '../index'; +import { describe, expect, it } from 'vitest'; + +/** + * @see https://github.com/lodash/lodash/blob/6a2cc1dfcf7634fea70d1bc5bd22db453df67b42/test/isLength.spec.js#L1 + */ +describe('isLength', () => { + it('should return `true` for lengths', () => { + const values = [0, 3, Number.MAX_SAFE_INTEGER]; + const expected = values.map(() => true); + const actual = values.map(isLength); + + expect(actual).toEqual(expected); + }); + + it('should return `false` for non-lengths', () => { + const values = [-1, '1', 1.1, Number.MAX_SAFE_INTEGER + 1]; + const expected = values.map(() => false); + const actual = values.map(isLength); + + expect(actual).toEqual(expected); + }); +}); diff --git a/src/predicate/index.ts b/src/predicate/index.ts index 9b8188f6..8f424732 100644 --- a/src/predicate/index.ts +++ b/src/predicate/index.ts @@ -3,4 +3,5 @@ export { isNil } from './isNil.ts'; export { isNotNil } from './isNotNil.ts'; export { isNull } from './isNull.ts'; export { isUndefined } from './isUndefined.ts'; -export { isPlainObject } from './isPlainObject.ts'; \ No newline at end of file +export { isLength } from './isLength.ts'; +export { isPlainObject } from './isPlainObject.ts'; diff --git a/src/predicate/isLength.spec.ts b/src/predicate/isLength.spec.ts new file mode 100644 index 00000000..778e9138 --- /dev/null +++ b/src/predicate/isLength.spec.ts @@ -0,0 +1,20 @@ +import { isLength } from './isLength'; +import { describe, expect, it } from 'vitest'; + +describe('isLength', () => { + it('should return `true` for lengths', () => { + const values = [0, 3, Number.MAX_SAFE_INTEGER]; + const expected = values.map(() => true); + const actual = values.map(isLength); + + expect(actual).toEqual(expected); + }); + + it('should return `false` for non-lengths', () => { + const values = [-1, '1', 1.1, Number.MAX_SAFE_INTEGER + 1]; + const expected = values.map(() => false); + const actual = values.map(isLength); + + expect(actual).toEqual(expected); + }); +}); diff --git a/src/predicate/isLength.ts b/src/predicate/isLength.ts new file mode 100644 index 00000000..f7961f78 --- /dev/null +++ b/src/predicate/isLength.ts @@ -0,0 +1,3 @@ +export function isLength(value: unknown): value is number { + return typeof value === 'number' && value > -1 && value % 1 === 0 && value <= Number.MAX_SAFE_INTEGER; +}