From 91f7ba183de4427ba788be41ea22d7dc7608c7b9 Mon Sep 17 00:00:00 2001 From: ssi02014 Date: Mon, 3 Jun 2024 23:36:00 +0900 Subject: [PATCH] =?UTF-8?q?feat(utils):=20contain=20=EC=8B=A0=EA=B7=9C=20?= =?UTF-8?q?=EC=9C=A0=ED=8B=B8=20=ED=95=A8=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .changeset/unlucky-rules-shop.md | 5 ++ docs/docs/utils/array/contain.md | 74 +++++++++++++++++++ .../utils/src/array/contain/contain.spec.ts | 40 ++++++++++ packages/utils/src/array/contain/index.ts | 7 ++ packages/utils/src/array/index.ts | 3 +- 5 files changed, 128 insertions(+), 1 deletion(-) create mode 100644 .changeset/unlucky-rules-shop.md create mode 100644 docs/docs/utils/array/contain.md create mode 100644 packages/utils/src/array/contain/contain.spec.ts create mode 100644 packages/utils/src/array/contain/index.ts diff --git a/.changeset/unlucky-rules-shop.md b/.changeset/unlucky-rules-shop.md new file mode 100644 index 000000000..67dda24e4 --- /dev/null +++ b/.changeset/unlucky-rules-shop.md @@ -0,0 +1,5 @@ +--- +'@modern-kit/utils': minor +--- + +feat(utils): contain 신규 유틸 함수 추가 - @ssi02014 diff --git a/docs/docs/utils/array/contain.md b/docs/docs/utils/array/contain.md new file mode 100644 index 000000000..f5126db5f --- /dev/null +++ b/docs/docs/utils/array/contain.md @@ -0,0 +1,74 @@ +# contain + +첫 번째 인자로 넘긴 배열에 특정 요소가 포함되어 있는지 판단하는 유틸 함수입니다. + +includes의 `as const`를 활용 했을 때, 타입이 호환되지 않은 요소가 포함되어 있는지 확인 할 때 타입 에러가 발생하는 문제점을 `some` 메서드를 활용해 개선하기 위한 함수입니다. + +```ts title="typescript" +const arr = [1, 2, 3] as const + +arr.includes(4); // '4' 형식의 인수는 '1 | 2 | 3' 형식의 매개 변수에 할당될 수 없습니다. +``` + +`some` 메서드를 통해 요소가 포함되어 있는지 판단 할 때 기본적으로 `Object.is` 메서드를 활용합니다. 단, 필요 시에 3번째 인자로 `comparator` 함수를 활용 할 수 있습니다. + + +## Code + +[🔗 실제 구현 코드 확인](https://github.com/modern-agile-team/modern-kit/blob/main/packages/utils/src/array/contain/index.ts) + +## Interface + +```ts title="typescript" +const contain: ( + arr: readonly T[] | T[], + value: unknown, + comparator?: (x: any, y: any) => boolean // default: Object.is +) => value is T; +``` + +## Usage +### Default +```ts title="typescript" +import { contain } from '@modern-kit/utils'; + +const arr = [0, 1, 2, 3, NaN, {}]; + +contain(arr, 1); // true +contain(arr, NaN); // true + +contain(arr, -0); // false +contain(arr, 4); // false +contain(arr, "3"); // false +contain(arr, {}); // false +``` + +### Comparator +```ts title="typescript" +const arr = [{ a: 1, b: 2 }]; + +contain(arr, { a: 1, c: 2 }, (x, y) => x.a === y.a); // true +contain( + arr, + { a: 1, b: 2 }, + (x, y) => JSON.stringify(x) === JSON.stringify(y) +); // true +``` + +### Narrowing types +```ts title="typescript" +const arr = [2, 3, 'foo'] as const; +const value = 'foo' as unknown; + +if (contain(arr, value)) { + value; // 2 | 3 | 'foo' +} else { + value; // unknown +} +``` + +## Note +- [Object.is(en) - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is) +- [Object.is(ko) - MDN](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/is) +- [동등 비교 및 동일성(en) - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness) +- [동등 비교 및 동일성(ko) - MDN](https://developer.mozilla.org/ko/docs/Web/JavaScript/Equality_comparisons_and_sameness#%EB%8F%99%EC%9D%BC_%EA%B0%92_%EC%A0%9C%EB%A1%9C_%EB%8F%99%EB%93%B1) \ No newline at end of file diff --git a/packages/utils/src/array/contain/contain.spec.ts b/packages/utils/src/array/contain/contain.spec.ts new file mode 100644 index 000000000..fb76f25fb --- /dev/null +++ b/packages/utils/src/array/contain/contain.spec.ts @@ -0,0 +1,40 @@ +import { contain } from '.'; + +describe('contain', () => { + it('should return true if the array contains the element, otherwise return false.', () => { + const arr = [0, 1, 2, 'foo', NaN, {}]; + + expect(contain(arr, 1)).toBeTruthy(); + expect(contain(arr, 'foo')).toBeTruthy(); + expect(contain(arr, NaN)).toBeTruthy(); + + expect(contain(arr, 4)).toBeFalsy(); + expect(contain(arr, {})).toBeFalsy(); + expect(contain(arr, -0)).toBeFalsy(); + expect(contain(arr, '2')).toBeFalsy(); + }); + + it('should determine inclusion based on the result of the comparator function if provided.', () => { + const arr = [{ a: 1, b: 2 }]; + + expect(contain(arr, { a: 1, c: 2 }, (x, y) => x.a === y.a)).toBeTruthy(); + expect( + contain( + arr, + { a: 1, b: 2 }, + (x, y) => JSON.stringify(x) === JSON.stringify(y) + ) + ).toBeTruthy(); + }); + + it('should perform type narrowing with conditional expressions.', () => { + const arr = [2, 3, 'foo'] as const; + const value = 'foo' as unknown; + + if (contain(arr, value)) { + expectTypeOf(value).toEqualTypeOf<2 | 3 | 'foo'>(); + } else { + expectTypeOf(value).toEqualTypeOf(); + } + }); +}); diff --git a/packages/utils/src/array/contain/index.ts b/packages/utils/src/array/contain/index.ts new file mode 100644 index 000000000..31cdf282a --- /dev/null +++ b/packages/utils/src/array/contain/index.ts @@ -0,0 +1,7 @@ +export const contain = ( + arr: T[] | ReadonlyArray, + value: unknown, + comparator: (x: any, y: any) => boolean = Object.is +): value is T => { + return arr.some((item) => comparator(item, value)); +}; diff --git a/packages/utils/src/array/index.ts b/packages/utils/src/array/index.ts index 6a5b137ba..47e2b6279 100644 --- a/packages/utils/src/array/index.ts +++ b/packages/utils/src/array/index.ts @@ -1,3 +1,4 @@ +export * from './chunk'; +export * from './contain'; export * from './countOccurrencesInArray'; export * from './excludeElements'; -export * from './chunk';