Skip to content

Commit

Permalink
feat(isArrayLike,isFunction): Add isArrayLike and isFunction with com…
Browse files Browse the repository at this point in the history
…patibility (#265)

* add isArrayLike and isFunction

* Add bench

* Add create arguments function

* Add isArrayLike function

* Add compatibility

* Change sub title to korean in isLength docs

* Add docs

* Fix typo error

* add toArgs testcase and remove a unusable expression

---------

Co-authored-by: Sojin Park <[email protected]>
  • Loading branch information
dayongkr and raon0211 authored Jul 21, 2024
1 parent 1043a48 commit bd7cb34
Show file tree
Hide file tree
Showing 21 changed files with 487 additions and 20 deletions.
26 changes: 26 additions & 0 deletions benchmarks/performance/isArrayLike.bench.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { bench, describe } from 'vitest';
import { isArrayLike as isArrayLikeToolkit } from 'es-toolkit';
import { isArrayLike as isArrayLikeLodash } from 'lodash';

describe('isArrayLike', () => {
bench('es-toolkit/isArrayLike', () => {
isArrayLikeToolkit(true);
isArrayLikeToolkit(new Date());
isArrayLikeToolkit(new Error());
isArrayLikeToolkit({ a: 1 });
isArrayLikeToolkit(1);
isArrayLikeToolkit(/x/);
isArrayLikeToolkit(Array.from({ length: 10000 }));
isArrayLikeToolkit({ length: 1000 });
});
bench('lodash/isArrayLike', () => {
isArrayLikeLodash(true);
isArrayLikeLodash(new Date());
isArrayLikeLodash(new Error());
isArrayLikeLodash({ a: 1 });
isArrayLikeLodash(1);
isArrayLikeLodash(/x/);
isArrayLikeLodash(Array.from({ length: 10000 }));
isArrayLikeLodash({ length: 1000 });
});
});
36 changes: 36 additions & 0 deletions benchmarks/performance/isFunction.bench.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { bench, describe } from 'vitest';
import { isFunction as isFunctionToolkit } from 'es-toolkit';
import { isFunction as isFunctionLodash } from 'lodash';

describe('isFunction', () => {
bench('es-toolkit/isFunction', () => {
isFunctionToolkit(true);
isFunctionToolkit(new Date());
isFunctionToolkit(new Error());
isFunctionToolkit({ a: 1 });
isFunctionToolkit(1);
isFunctionToolkit(/x/);
isFunctionToolkit(Array.from({ length: 10000 }));
isFunctionToolkit(async function () {});
isFunctionToolkit(function* () {});
isFunctionToolkit(Proxy);
isFunctionToolkit(Int8Array);
isFunctionToolkit(() => {});
isFunctionToolkit(Array.prototype.slice);
});
bench('lodash/isFunction', () => {
isFunctionLodash(true);
isFunctionLodash(new Date());
isFunctionLodash(new Error());
isFunctionLodash({ a: 1 });
isFunctionLodash(1);
isFunctionLodash(/x/);
isFunctionLodash(Array.from({ length: 10000 }));
isFunctionLodash(async function () {});
isFunctionLodash(function* () {});
isFunctionLodash(Proxy);
isFunctionLodash(Int8Array);
isFunctionLodash(() => {});
isFunctionLodash(Array.prototype.slice);
});
});
2 changes: 2 additions & 0 deletions docs/.vitepress/en.mts
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,9 @@ function sidebar(): DefaultTheme.Sidebar {
text: 'Predicates',
items: [
{ text: 'isArray (compat)', link: '/reference/compat/predicate/isArray' },
{ text: 'isArrayLike', link: '/reference/predicate/isArrayLike' },
{ text: 'isEqual', link: '/reference/predicate/isEqual' },
{ text: 'isFunction', link: '/reference/predicate/isFunction' },
{ text: 'isLength', link: '/reference/predicate/isLength' },
{ text: 'isPlainObject', link: '/reference/predicate/isPlainObject' },
{ text: 'isNil', link: '/reference/predicate/isNil' },
Expand Down
2 changes: 2 additions & 0 deletions docs/.vitepress/ko.mts
Original file line number Diff line number Diff line change
Expand Up @@ -162,8 +162,10 @@ function sidebar(): DefaultTheme.Sidebar {
text: '타입 가드',
items: [
{ text: 'isArray (호환성)', link: '/ko/reference/compat/predicate/isArray' },
{ text: 'isArrayLike', link: '/ko/reference/predicate/isArrayLike' },
{ text: 'isEqual', link: '/ko/reference/predicate/isEqual' },
{ text: 'isLength', link: '/ko/reference/predicate/isLength' },
{ text: 'isFunction', link: '/ko/reference/predicate/isFunction' },
{ text: 'isPlainObject', link: '/ko/reference/predicate/isPlainObject' },
{ text: 'isNil', link: '/ko/reference/predicate/isNil' },
{ text: 'isNotNil', link: '/ko/reference/predicate/isNotNil' },
Expand Down
4 changes: 2 additions & 2 deletions docs/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ Even if a feature is marked "in review," it might already be under review to ens
| [isArguments](https://lodash.com/docs/4.17.15#isArguments) ||
| [isArray](https://lodash.com/docs/4.17.15#isArray) ||
| [isArrayBuffer](https://lodash.com/docs/4.17.15#isArrayBuffer) ||
| [isArrayLike](https://lodash.com/docs/4.17.15#isArrayLike) | |
| [isArrayLike](https://lodash.com/docs/4.17.15#isArrayLike) | |
| [isArrayLikeObject](https://lodash.com/docs/4.17.15#isArrayLikeObject) ||
| [isBoolean](https://lodash.com/docs/4.17.15#isBoolean) ||
| [isBuffer](https://lodash.com/docs/4.17.15#isBuffer) ||
Expand All @@ -208,7 +208,7 @@ Even if a feature is marked "in review," it might already be under review to ens
| [isEqualWith](https://lodash.com/docs/4.17.15#isEqualWith) ||
| [isError](https://lodash.com/docs/4.17.15#isError) ||
| [isFinite](https://lodash.com/docs/4.17.15#isFinite) ||
| [isFunction](https://lodash.com/docs/4.17.15#isFunction) | |
| [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) ||
| [isMap](https://lodash.com/docs/4.17.15#isMap) ||
Expand Down
4 changes: 2 additions & 2 deletions docs/ko/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ chunk([1, 2, 3, 4], 0);
| [isArguments](https://lodash.com/docs/4.17.15#isArguments) ||
| [isArray](https://lodash.com/docs/4.17.15#isArray) ||
| [isArrayBuffer](https://lodash.com/docs/4.17.15#isArrayBuffer) ||
| [isArrayLike](https://lodash.com/docs/4.17.15#isArrayLike) | |
| [isArrayLike](https://lodash.com/docs/4.17.15#isArrayLike) | |
| [isArrayLikeObject](https://lodash.com/docs/4.17.15#isArrayLikeObject) ||
| [isBoolean](https://lodash.com/docs/4.17.15#isBoolean) ||
| [isBuffer](https://lodash.com/docs/4.17.15#isBuffer) ||
Expand All @@ -209,7 +209,7 @@ chunk([1, 2, 3, 4], 0);
| [isEqualWith](https://lodash.com/docs/4.17.15#isEqualWith) ||
| [isError](https://lodash.com/docs/4.17.15#isError) ||
| [isFinite](https://lodash.com/docs/4.17.15#isFinite) ||
| [isFunction](https://lodash.com/docs/4.17.15#isFunction) | |
| [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) ||
| [isMap](https://lodash.com/docs/4.17.15#isMap) ||
Expand Down
34 changes: 34 additions & 0 deletions docs/ko/reference/predicate/isArrayLike.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# isArrayLike

주어진 값이 유사 배열인지 확인해요.

유사 배열 객체는 `null`이나 `undefined`나 함수가 아니며, `length` 프로퍼티가 유효한 길이인 객체에요.

TypeScript의 타입 가드로 사용할 수 있어요. 파라미터로 주어진 값의 타입을 `ArrayLike<unknown>`로 좁혀요.

## 인터페이스

```typescript
function isArrayLike(value: unknown): value is ArrayLike<unknown>;
```

### 파라미터

- `value` (`unknown`): 유사 배열 객체인지 확인할 값이에요.

### 반환 값

(`value is number`): 주어진 값이 유사 배열 객체이면 `true`, 아니면 `false`를 반환해요.

## 예시

```typescript
import { isArrayLike } from 'es-toolkit/predicate';

console.log(isArrayLike([1, 2, 3])); // true
console.log(isArrayLike('abc')); // true
console.log(isArrayLike({ 0: 'a', length: 1 })); // true
console.log(isArrayLike({})); // false
console.log(isArrayLike(null)); // false
console.log(isArrayLike(undefined)); // false
```
33 changes: 33 additions & 0 deletions docs/ko/reference/predicate/isFunction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# isFunction

주어진 값이 함수인지 확인해요.

`value`가 함수이면 `true`, 아니면 `false`를 반환해요.

TypeScript의 타입 가드로 사용할 수 있어요. 파라미터로 주어진 값의 타입을 `(...args: unknown[]) => unknown`로 좁혀요.

## 인터페이스

```typescript
function isFunction(value: unknown): value is (...args: unknown[]) => unknown;
```

### 파라미터

- `value` (`unknown`): 함수인지 확인할 값이에요.

### 반환 값

(`value is number`): 주어진 값이 함수이면 `true`, 아니면 `false`를 반환해요.

## 예시

```typescript
import { isFunction } from 'es-toolkit/predicate';

console.log(isFunction(Array.prototype.slice)); // true
console.log(isFunction(async function () {})); // true
console.log(isFunction(function* () {})); // true
console.log(isFunction(Proxy)); // true
console.log(isFunction(Int8Array)); // true
```
10 changes: 5 additions & 5 deletions docs/ko/reference/predicate/isLength.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,25 @@

주어진 값이 유효한 길이인지 확인해요.

유효한 길이란, `0` 이상 `Number.MAX_SAFE_INTEGER` 미만의 정수를 말해요.
유효한 길이란, `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';
Expand Down
34 changes: 34 additions & 0 deletions docs/reference/predicate/isArrayLike.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# isArrayLike

Check if a value is an array-like object.

An array-like object is an object that is not `null` or `undefined` or a function, and has a `length` property that is a valid length.

This function can also serve as a type predicate in TypeScript, narrowing the type of the argument to an array-like object.

## Signature

```typescript
function isArrayLike(value: unknown): value is ArrayLike<unknown>;
```

### Parameters

- `value` (`unknown`): The value to check if it is an array-like object.

### Returns

(`value is ArrayLike<unknown>`): Returns `true` if the value is an array-like object, otherwise `false`.

## Examples

```typescript
import { isArrayLike } from 'es-toolkit/predicate';

console.log(isArrayLike([1, 2, 3])); // true
console.log(isArrayLike('abc')); // true
console.log(isArrayLike({ 0: 'a', length: 1 })); // true
console.log(isArrayLike({})); // false
console.log(isArrayLike(null)); // false
console.log(isArrayLike(undefined)); // false
```
33 changes: 33 additions & 0 deletions docs/reference/predicate/isFunction.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# isFunction

Checks if `value` is a function.

This function returns `true` if `value` is a function, and `false` otherwise.

This function can also serve as a type predicate in TypeScript, narrowing the type of the argument to a function.

## Signature

```typescript
function isFunction(value: unknown): value is (...args: unknown[]) => unknown;
```

### Parameters

- `value` (`unknown`): The value to check if it is a function.

### Returns

(`value is (...args: unknown[]) => unknown`): Returns `true` if the value is a function, otherwise `false`.

## Examples

```typescript
import { isFunction } from 'es-toolkit/predicate';

console.log(isFunction(Array.prototype.slice)); // true
console.log(isFunction(async function () {})); // true
console.log(isFunction(function* () {})); // true
console.log(isFunction(Proxy)); // true
console.log(isFunction(Int8Array)); // true
```
6 changes: 3 additions & 3 deletions docs/zh_hans/compatibility.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ chunk([1, 2, 3, 4], 0);
| [isArguments](https://lodash.com/docs/4.17.15#isArguments) ||
| [isArray](https://lodash.com/docs/4.17.15#isArray) ||
| [isArrayBuffer](https://lodash.com/docs/4.17.15#isArrayBuffer) ||
| [isArrayLike](https://lodash.com/docs/4.17.15#isArrayLike) | |
| [isArrayLike](https://lodash.com/docs/4.17.15#isArrayLike) | |
| [isArrayLikeObject](https://lodash.com/docs/4.17.15#isArrayLikeObject) ||
| [isBoolean](https://lodash.com/docs/4.17.15#isBoolean) ||
| [isBuffer](https://lodash.com/docs/4.17.15#isBuffer) ||
Expand All @@ -208,9 +208,9 @@ chunk([1, 2, 3, 4], 0);
| [isEqualWith](https://lodash.com/docs/4.17.15#isEqualWith) ||
| [isError](https://lodash.com/docs/4.17.15#isError) ||
| [isFinite](https://lodash.com/docs/4.17.15#isFinite) ||
| [isFunction](https://lodash.com/docs/4.17.15#isFunction) | |
| [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) ||
Expand Down
16 changes: 16 additions & 0 deletions src/compat/_internal/toArgs.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { describe, it, expect } from 'vitest';
import { toArgs } from './toArgs';

describe('toArgs', () => {
it('converts an array to an arguments object', () => {
const result = toArgs([1, 2, 3]);

expect(result.toString()).toBe('[object Arguments]');

// eslint-disable-next-line @typescript-eslint/no-unused-vars
(function (..._args) {
// eslint-disable-next-line prefer-rest-params
expect(arguments).toEqual(result);
})(1, 2, 3);
});
});
20 changes: 12 additions & 8 deletions src/compat/_internal/toArgs.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
/**
* Converts `array` to an `arguments` object.
* Converts an array to an `arguments` object.
*
* @private
* @param {Array} array The array to convert.
* @returns {Object} Returns the converted `arguments` object.
* @param {unknown[]} array - The array to convert.
* @returns {IArguments} - The `arguments` object.
*
* @example
* toArgs([1, 2, 3]); // { '0': 1, '1': 2, '2': 3 } as IArguments
*/
export function toArgs(array: any[]) {
return function (..._: any[]) {
export function toArgs(array: unknown[]): IArguments {
// eslint-disable-next-line prefer-spread, @typescript-eslint/no-unused-vars
return (function (..._: any[]) {
// eslint-disable-next-line prefer-rest-params
return arguments;
}.apply(undefined, array);
}
})(...array);
}
38 changes: 38 additions & 0 deletions src/compat/predicate/isArrayLike.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { describe, expect, it } from 'vitest';
import { isArrayLike } from '../../predicate/isArrayLike';
import { falsey } from '../_internal/falsey';
import { args } from '../_internal/args';

describe('isArrayLike', () => {
it('should return `true` for array-like values', () => {
const values = [args, [1, 2, 3], { 0: 'a', length: 1 }, 'a'];
const expected = values.map(() => true);
const actual = values.map(isArrayLike);

expect(actual).toEqual(expected);
});

it('should return `false` for non-arrays', () => {
const expected = falsey.map(value => value === '');

const actual = falsey.map(isArrayLike);

expect(actual).toEqual(expected);

const slice = Array.prototype.slice;
const asyncFunc = async function () {};
const genFunc = function* () {};
const symbol = Symbol ? Symbol('a') : undefined;

expect(isArrayLike(true)).toBe(false);
expect(isArrayLike(new Date())).toBe(false);
expect(isArrayLike(new Error())).toBe(false);
expect(isArrayLike(asyncFunc)).toBe(false);
expect(isArrayLike(genFunc)).toBe(false);
expect(isArrayLike(slice)).toBe(false);
expect(isArrayLike({ a: 1 })).toBe(false);
expect(isArrayLike(1)).toBe(false);
expect(isArrayLike(/x/)).toBe(false);
expect(isArrayLike(symbol)).toBe(false);
});
});
Loading

0 comments on commit bd7cb34

Please sign in to comment.