-
Notifications
You must be signed in to change notification settings - Fork 343
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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>
- Loading branch information
1 parent
85d58ec
commit 984d567
Showing
13 changed files
with
394 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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'); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export const stubFour = function () { | ||
return 4; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export const stubThree = function () { | ||
return 3; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export const stubTwo = function () { | ||
return 2; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); | ||
}); | ||
}); | ||
}); |
Oops, something went wrong.