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:
D-Sketon 2024-12-18 14:08:52 +08:00 committed by GitHub
parent 85d58ec609
commit 984d56728e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 394 additions and 1 deletions

View 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);
});
});

View 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');
});
});

View 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
```

View 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
```

View 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 isnt 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
```

View 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
```

View File

@ -0,0 +1,3 @@
export const stubFour = function () {
return 4;
};

View File

@ -0,0 +1,3 @@
export const stubThree = function () {
return 3;
};

View File

@ -0,0 +1,3 @@
export const stubTwo = function () {
return 2;
};

View File

@ -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';

View File

@ -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.

View 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);
});
});
});

View 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);
};
}