mirror of
https://github.com/toss/es-toolkit.git
synced 2024-12-25 17:04:22 +03:00
feat(compat): implement methodOf (#907)
* feat(compat): implement methodOf * make lint happy * Update docs/ko/reference/compat/util/methodOf.md * Update docs/ja/reference/compat/util/methodOf.md * Update docs/ko/reference/compat/util/methodOf.md --------- Co-authored-by: Sojin Park <raon0211@toss.im> Co-authored-by: Sojin Park <raon0211@gmail.com>
This commit is contained in:
parent
85d58ec609
commit
984d56728e
24
benchmarks/performance/method.bench.ts
Normal file
24
benchmarks/performance/method.bench.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { bench, describe } from 'vitest';
|
||||
import { method as methodToolkit_ } from 'es-toolkit/compat';
|
||||
import { method as methodLodash_ } from 'lodash';
|
||||
|
||||
const methodToolkit = methodToolkit_;
|
||||
const methodLodash = methodLodash_;
|
||||
|
||||
const object = {
|
||||
a: {
|
||||
b: function (x: number, y: number) {
|
||||
return x + y;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
describe('method', () => {
|
||||
bench('es-toolkit/compat', () => {
|
||||
methodToolkit('a.b', 1, 2)(object);
|
||||
});
|
||||
|
||||
bench('lodash', () => {
|
||||
methodLodash('a.b', 1, 2)(object);
|
||||
});
|
||||
});
|
24
benchmarks/performance/methodOf.bench.ts
Normal file
24
benchmarks/performance/methodOf.bench.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { bench, describe } from 'vitest';
|
||||
import { methodOf as methodOfToolkit_ } from 'es-toolkit/compat';
|
||||
import { methodOf as methodOfLodash_ } from 'lodash';
|
||||
|
||||
const methodOfToolkit = methodOfToolkit_;
|
||||
const methodOfLodash = methodOfLodash_;
|
||||
|
||||
const object = {
|
||||
a: {
|
||||
b: function (x: number, y: number) {
|
||||
return x + y;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
describe('methodOf', () => {
|
||||
bench('es-toolkit/compat', () => {
|
||||
methodOfToolkit(object, 1, 2)('a.b');
|
||||
});
|
||||
|
||||
bench('lodash', () => {
|
||||
methodOfLodash(object, 1, 2)('a.b');
|
||||
});
|
||||
});
|
39
docs/ja/reference/compat/util/methodOf.md
Normal file
39
docs/ja/reference/compat/util/methodOf.md
Normal file
@ -0,0 +1,39 @@
|
||||
# methodOf
|
||||
|
||||
::: info
|
||||
この関数は互換性のために `es-toolkit/compat` からのみインポートできます。代替可能なネイティブ JavaScript API があるか、まだ十分に最適化されていないためです。
|
||||
|
||||
`es-toolkit/compat` からこの関数をインポートすると、[lodash と完全に同じように動作](../../../compatibility.md)します。
|
||||
:::
|
||||
|
||||
指定された`object`のパスにあるメソッドを、提供された引数で呼び出す関数を作成します。
|
||||
|
||||
## インターフェース
|
||||
|
||||
```typescript
|
||||
function methodOf(object: object, ...args: any[]): (path: PropertyKey | PropertyKey[]) => any;
|
||||
```
|
||||
|
||||
### パラメータ
|
||||
|
||||
- `object` (`object`): 要検索のオブジェクト。
|
||||
- `args` (`...any`): メソッドを呼び出す際に使用する引数。
|
||||
|
||||
### 戻り値
|
||||
|
||||
(`(path: PropertyKey | PropertyKey[]) => any`): パスを受け取り、`object` の `path` で `args` でメソッドを呼び出す新しい関数。
|
||||
|
||||
## 例
|
||||
|
||||
```typescript
|
||||
const object = {
|
||||
a: {
|
||||
b: function (x, y) {
|
||||
return x + y;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const add = methodOf(object, 1, 2);
|
||||
console.log(add('a.b')); // => 3
|
||||
```
|
39
docs/ko/reference/compat/util/methodOf.md
Normal file
39
docs/ko/reference/compat/util/methodOf.md
Normal file
@ -0,0 +1,39 @@
|
||||
# methodOf
|
||||
|
||||
::: info
|
||||
이 함수는 호환성을 위한 `es-toolkit/compat` 에서만 가져올 수 있어요. 대체할 수 있는 네이티브 JavaScript API가 있거나, 아직 충분히 최적화되지 않았기 때문이에요.
|
||||
|
||||
`es-toolkit/compat`에서 이 함수를 가져오면, [lodash와 완전히 똑같이 동작](../../../compatibility.md)해요.
|
||||
:::
|
||||
|
||||
주어진 경로에 있는 객체의 메서드를 제공된 파라미터로 호출하는 함수를 만들어요.
|
||||
|
||||
## 인터페이스
|
||||
|
||||
```typescript
|
||||
function methodOf(object: object, ...args: any[]): (path: PropertyKey | PropertyKey[]) => any;
|
||||
```
|
||||
|
||||
### 파라미터
|
||||
|
||||
- `object` (`object`): 조회할 객체.
|
||||
- `args` (`...any`): 메서드를 호출할 때 사용할 인수.
|
||||
|
||||
### 반환 값
|
||||
|
||||
(`(path: PropertyKey | PropertyKey[]) => any`): 경로를 받아 `object`의 `path`에서 `args`로 메서드를 호출하는 새로운 함수.
|
||||
|
||||
## 예시
|
||||
|
||||
```typescript
|
||||
const object = {
|
||||
a: {
|
||||
b: function (x, y) {
|
||||
return x + y;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const add = methodOf(object, 1, 2);
|
||||
console.log(add('a.b')); // => 3
|
||||
```
|
39
docs/reference/compat/util/methodOf.md
Normal file
39
docs/reference/compat/util/methodOf.md
Normal file
@ -0,0 +1,39 @@
|
||||
# methodOf
|
||||
|
||||
::: info
|
||||
This function is only available in `es-toolkit/compat` for compatibility reasons. It either has alternative native JavaScript APIs or isn’t fully optimized yet.
|
||||
|
||||
When imported from `es-toolkit/compat`, it behaves exactly like lodash and provides the same functionalities, as detailed [here](../../../compatibility.md).
|
||||
:::
|
||||
|
||||
Creates a function that invokes the method at a given path of `object` with the provided arguments.
|
||||
|
||||
## Signature
|
||||
|
||||
```typescript
|
||||
function methodOf(object: object, ...args: any[]): (path: PropertyKey | PropertyKey[]) => any;
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
- `object` (`object`): The object to query.
|
||||
- `args` (`...any`): The arguments to invoke the method with.
|
||||
|
||||
### Returns
|
||||
|
||||
(`(path: PropertyKey | PropertyKey[]) => any`): Returns a new function that takes a path and invokes the method at `path` of `object` with `args`.
|
||||
|
||||
## Examples
|
||||
|
||||
```typescript
|
||||
const object = {
|
||||
a: {
|
||||
b: function (x, y) {
|
||||
return x + y;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const add = methodOf(object, 1, 2);
|
||||
console.log(add('a.b')); // => 3
|
||||
```
|
39
docs/zh_hans/reference/compat/util/methodOf.md
Normal file
39
docs/zh_hans/reference/compat/util/methodOf.md
Normal file
@ -0,0 +1,39 @@
|
||||
# methodOf
|
||||
|
||||
::: info
|
||||
出于兼容性原因,此函数仅在 `es-toolkit/compat` 中提供。它可能具有替代的原生 JavaScript API,或者尚未完全优化。
|
||||
|
||||
从 `es-toolkit/compat` 导入时,它的行为与 lodash 完全一致,并提供相同的功能,详情请见 [这里](../../../compatibility.md)。
|
||||
:::
|
||||
|
||||
创建一个函数,该函数使用提供的参数调用指定`object`路径上的方法。
|
||||
|
||||
## 签名
|
||||
|
||||
```typescript
|
||||
function methodOf(object: object, ...args: any[]): (path: PropertyKey | PropertyKey[]) => any;
|
||||
```
|
||||
|
||||
### 参数
|
||||
|
||||
- `object` (`object`): 要查询的对象。
|
||||
- `args` (`...any`): 用来调用方法的参数。
|
||||
|
||||
### 返回值
|
||||
|
||||
(`(path: PropertyKey | PropertyKey[]) => any`): 返回一个新函数,该函数接受一个路径,并用`args`在`object`的`path`调用方法。
|
||||
|
||||
## 示例
|
||||
|
||||
```typescript
|
||||
const object = {
|
||||
a: {
|
||||
b: function (x, y) {
|
||||
return x + y;
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const add = methodOf(object, 1, 2);
|
||||
console.log(add('a.b')); // => 3
|
||||
```
|
3
src/compat/_internal/stubFour.ts
Normal file
3
src/compat/_internal/stubFour.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export const stubFour = function () {
|
||||
return 4;
|
||||
};
|
3
src/compat/_internal/stubThree.ts
Normal file
3
src/compat/_internal/stubThree.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export const stubThree = function () {
|
||||
return 3;
|
||||
};
|
3
src/compat/_internal/stubTwo.ts
Normal file
3
src/compat/_internal/stubTwo.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export const stubTwo = function () {
|
||||
return 2;
|
||||
};
|
@ -208,6 +208,7 @@ export { iteratee } from './util/iteratee.ts';
|
||||
export { lt } from './util/lt.ts';
|
||||
export { lte } from './util/lte.ts';
|
||||
export { method } from './util/method.ts';
|
||||
export { methodOf } from './util/methodOf.ts';
|
||||
export { now } from './util/now.ts';
|
||||
export { stubArray } from './util/stubArray.ts';
|
||||
export { stubFalse } from './util/stubFalse.ts';
|
||||
|
@ -1,5 +1,4 @@
|
||||
import { invoke } from './invoke.ts';
|
||||
import { flatten } from '../array/flatten.ts';
|
||||
|
||||
/**
|
||||
* Creates a function that invokes the method at `path` of a given object with the provided arguments.
|
||||
|
154
src/compat/util/methodOf.spec.ts
Normal file
154
src/compat/util/methodOf.spec.ts
Normal file
@ -0,0 +1,154 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { constant, each, map, noop } from '..';
|
||||
import { methodOf as methodOfToolkit } from './methodOf';
|
||||
import { times } from './times';
|
||||
import { stubFour } from '../_internal/stubFour';
|
||||
import { stubOne } from '../_internal/stubOne';
|
||||
import { stubThree } from '../_internal/stubThree';
|
||||
import { stubTwo } from '../_internal/stubTwo';
|
||||
|
||||
describe('methodOf', () => {
|
||||
it('should create a function that calls a method of a given key', () => {
|
||||
const object = { a: stubOne };
|
||||
|
||||
each(['a', ['a']], path => {
|
||||
const methodOf = methodOfToolkit(object);
|
||||
expect(methodOf.length).toBe(1);
|
||||
expect(methodOf(path)).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with deep property values', () => {
|
||||
const object = { a: { b: stubTwo } };
|
||||
|
||||
each(['a.b', ['a', 'b']], path => {
|
||||
const methodOf = methodOfToolkit(object);
|
||||
expect(methodOf(path)).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with a non-string `path`', () => {
|
||||
const array = times(3, constant);
|
||||
|
||||
each([1, [1]], path => {
|
||||
const methodOf = methodOfToolkit(array);
|
||||
expect(methodOf(path)).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should coerce `path` to a string', () => {
|
||||
function fn() {}
|
||||
fn.toString = constant('fn');
|
||||
|
||||
const expected = [1, 2, 3, 4];
|
||||
const object = {
|
||||
null: stubOne,
|
||||
undefined: stubTwo,
|
||||
fn: stubThree,
|
||||
'[object Object]': stubFour,
|
||||
};
|
||||
const paths = [null, undefined, fn, {}];
|
||||
|
||||
times(2, index => {
|
||||
const actual = map(paths, path => {
|
||||
const methodOf = methodOfToolkit(object);
|
||||
// @ts-expect-error - methodOf should handle nullish values
|
||||
return methodOf(index ? [path] : path);
|
||||
});
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
it('should work with inherited property values', () => {
|
||||
function Foo() {}
|
||||
Foo.prototype.a = stubOne;
|
||||
|
||||
each(['a', ['a']], path => {
|
||||
// @ts-expect-error - Foo is a constructor
|
||||
const methodOf = methodOfToolkit(new Foo());
|
||||
expect(methodOf(path)).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should use a key over a path', () => {
|
||||
const object = { 'a.b': stubOne, a: { b: stubTwo } };
|
||||
|
||||
each(['a.b', ['a.b']], path => {
|
||||
const methodOf = methodOfToolkit(object);
|
||||
expect(methodOf(path)).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return `undefined` when `object` is nullish', () => {
|
||||
// eslint-disable-next-line no-sparse-arrays
|
||||
const values = [, null, undefined];
|
||||
const expected = map(values, noop);
|
||||
|
||||
each(['constructor', ['constructor']], path => {
|
||||
const actual = map(values, (value, index) => {
|
||||
// @ts-expect-error - methodOf should handle nullish values
|
||||
const methodOf = index ? methodOfToolkit() : methodOfToolkit(value);
|
||||
return methodOf(path);
|
||||
});
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return `undefined` for deep paths when `object` is nullish', () => {
|
||||
// eslint-disable-next-line no-sparse-arrays
|
||||
const values = [, null, undefined];
|
||||
const expected = map(values, noop);
|
||||
|
||||
each(['constructor.prototype.valueOf', ['constructor', 'prototype', 'valueOf']], path => {
|
||||
const actual = map(values, (value, index) => {
|
||||
// @ts-expect-error - methodOf should handle nullish values
|
||||
const methodOf = index ? methodOfToolkit() : methodOfToolkit(value);
|
||||
return methodOf(path);
|
||||
});
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return `undefined` if parts of `path` are missing', () => {
|
||||
const object = {};
|
||||
const methodOf = methodOfToolkit(object);
|
||||
|
||||
each(['a', 'a[1].b.c', ['a'], ['a', '1', 'b', 'c']], path => {
|
||||
expect(methodOf(path)).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
||||
it('should apply partial arguments to function', () => {
|
||||
const object = {
|
||||
fn: function () {
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
return Array.prototype.slice.call(arguments);
|
||||
},
|
||||
};
|
||||
|
||||
const methodOf = methodOfToolkit(object, 1, 2, 3);
|
||||
|
||||
each(['fn', ['fn']], path => {
|
||||
expect(methodOf(path)).toEqual([1, 2, 3]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should invoke deep property methods with the correct `this` binding', () => {
|
||||
const object = {
|
||||
a: {
|
||||
b: function () {
|
||||
return this.c;
|
||||
},
|
||||
c: 1,
|
||||
},
|
||||
};
|
||||
const methodOf = methodOfToolkit(object);
|
||||
|
||||
each(['a.b', ['a', 'b']], path => {
|
||||
expect(methodOf(path)).toBe(1);
|
||||
});
|
||||
});
|
||||
});
|
26
src/compat/util/methodOf.ts
Normal file
26
src/compat/util/methodOf.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import { invoke } from './invoke.ts';
|
||||
|
||||
/**
|
||||
* Creates a function that invokes the method at a given path of `object` with the provided arguments.
|
||||
*
|
||||
* @param {object} object - The object to query.
|
||||
* @param {...any} args - The arguments to invoke the method with.
|
||||
* @returns {(path: PropertyKey | PropertyKey[]) => any} - Returns a new function that takes a path and invokes the method at `path` with `args`.
|
||||
*
|
||||
* @example
|
||||
* const object = {
|
||||
* a: {
|
||||
* b: function (x, y) {
|
||||
* return x + y;
|
||||
* }
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* const add = methodOf(object, 1, 2);
|
||||
* console.log(add('a.b')); // => 3
|
||||
*/
|
||||
export function methodOf(object: object, ...args: any[]): (path: PropertyKey | PropertyKey[]) => any {
|
||||
return function (path: PropertyKey | PropertyKey[]) {
|
||||
return invoke(object, path, args);
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user