mirror of
https://github.com/toss/es-toolkit.git
synced 2024-11-24 11:45:26 +03:00
feat(spread): Implement spread (#428)
* feat(spread): implement spread * lodash compatibility * remove useless comment
This commit is contained in:
parent
5bccc517a1
commit
1c74848003
24
benchmarks/performance/spread.bench.ts
Normal file
24
benchmarks/performance/spread.bench.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { bench, describe } from 'vitest';
|
||||
import { spread as spreadToolkit } from 'es-toolkit';
|
||||
import { spread as spreadCompat } from 'es-toolkit/compat';
|
||||
import { spread as spreadLodash } from 'lodash';
|
||||
|
||||
describe('spread', () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function fn(_a: unknown, _b: unknown, _c: unknown) {
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
return Array.from(arguments);
|
||||
}
|
||||
|
||||
bench('es-toolkit/spread', () => {
|
||||
spreadToolkit(fn, 1);
|
||||
});
|
||||
|
||||
bench('es-toolkit/compat/spread', () => {
|
||||
spreadCompat(fn, 1);
|
||||
});
|
||||
|
||||
bench('lodash/spread', () => {
|
||||
spreadLodash(fn, 1);
|
||||
});
|
||||
});
|
@ -134,6 +134,7 @@ function sidebar(): DefaultTheme.Sidebar {
|
||||
{ text: 'partial', link: '/reference/function/partial' },
|
||||
{ text: 'partialRight', link: '/reference/function/partialRight' },
|
||||
{ text: 'rest', link: '/reference/function/rest' },
|
||||
{ text: 'spread', link: '/reference/function/spread' },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
@ -131,6 +131,7 @@ function sidebar(): DefaultTheme.Sidebar {
|
||||
{ text: 'partial', link: '/zh_hans/reference/function/partial' },
|
||||
{ text: 'partialRight', link: '/zh_hans/reference/function/partialRight' },
|
||||
{ text: 'rest', link: '/zh_hans/reference/function/rest' },
|
||||
{ text: 'spread', link: '/zh_hans/reference/function/spread' },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
51
docs/reference/function/spread.md
Normal file
51
docs/reference/function/spread.md
Normal file
@ -0,0 +1,51 @@
|
||||
# spread
|
||||
|
||||
Creates a function that invokes `func` with the `this` binding of the create function and an array of arguments much like [`Function#apply`](https://www.ecma-international.org/ecma-262/6.0/#sec-function.prototype.apply).
|
||||
|
||||
## Signature
|
||||
|
||||
```typescript
|
||||
function spread<F extends (...args: any[]) => any>(func: F, startIndex: number = 0): (...args: any[]) => ReturnType<F>;
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
- `func` (`F`): The function to spread arguments over.
|
||||
- `startIndex` (`number`, optional): The start position of the spread. Defaults to `0`.
|
||||
|
||||
### Returns
|
||||
|
||||
(`(...args: any[]) => ReturnType<F>`): A new function that invokes `func` with the spread arguments.
|
||||
|
||||
## Examples
|
||||
|
||||
```typescript
|
||||
import { spread } from 'es-toolkit/function';
|
||||
|
||||
const say = spread(function (who, what) {
|
||||
return who + ' says ' + what;
|
||||
});
|
||||
|
||||
say(['fred', 'hello']);
|
||||
// => 'fred says hello'
|
||||
```
|
||||
|
||||
## Lodash Compatibility
|
||||
|
||||
Import `spread` from `es-toolkit/compat` for full compatibility with lodash.
|
||||
|
||||
- `spread` treats `startIndex` as `0` for negative or `NaN` values.
|
||||
- `spread` accepts `startIndex` with fractional values, but coerces them to integers.
|
||||
|
||||
```typescript
|
||||
import { spread } from 'es-toolkit/compat';
|
||||
|
||||
function fn(a: unknown, b: unknown, c: unknown) {
|
||||
return Array.from(arguments);
|
||||
}
|
||||
|
||||
spread(fn, -1)([1, 2]); // Returns [1, 2]
|
||||
spread(fn, NaN)([1, 2]); // Returns [1, 2]
|
||||
spread(fn, 'a')([1, 2]); // Returns [1, 2]
|
||||
spread(fn, 1.6)(1, [2, 3]); // Returns [1, 2, 3]
|
||||
```
|
@ -53,10 +53,10 @@ console.log(chunk([1, 2, 3, 4, 5], 2));
|
||||
|
||||
## Lodash 兼容性
|
||||
|
||||
从`es-toolkit/compat`中导入`chunk`以实现与 lodash 的完全兼容。
|
||||
从 `es-toolkit/compat` 中导入 `chunk` 以实现与 lodash 的完全兼容。
|
||||
|
||||
- chunk 不抛出异常,当给定的`size`小于 1 时。
|
||||
- chunk 接受分数值,这些值将被向下舍入到最近的整数。
|
||||
- `chunk` 当给定的 `size` 小于 1 时不抛出异常。
|
||||
- `chunk` 接受分数值,这些值将被向下舍入到最近的整数。
|
||||
|
||||
```typescript
|
||||
import { chunk } from 'es-toolkit/compat';
|
||||
|
51
docs/zh_hans/reference/function/spread.md
Normal file
51
docs/zh_hans/reference/function/spread.md
Normal file
@ -0,0 +1,51 @@
|
||||
# spread
|
||||
|
||||
创建一个函数,该函数调用 `func`,使用创建函数的 `this` 绑定和一个类似于 [`Function#apply`](https://www.ecma-international.org/ecma-262/6.0/#sec-function.prototype.apply) 的参数数组。
|
||||
|
||||
## 签名
|
||||
|
||||
```typescript
|
||||
function spread<F extends (...args: any[]) => any>(func: F, startIndex: number = 0): (...args: any[]) => ReturnType<F>;
|
||||
```
|
||||
|
||||
### 参数
|
||||
|
||||
- `func` (`F`): 用于展开参数的函数。
|
||||
- `startIndex` (`number`, 可选): 参数的开始位置,默认为 `0`。
|
||||
|
||||
### 返回值
|
||||
|
||||
(`(...args: any[]) => ReturnType<F>`): 一个新的函数,该函数使用展开的参数调用 `func`。
|
||||
|
||||
## 示例
|
||||
|
||||
```typescript
|
||||
import { spread } from 'es-toolkit/function';
|
||||
|
||||
const say = spread(function (who, what) {
|
||||
return who + ' says ' + what;
|
||||
});
|
||||
|
||||
say(['fred', 'hello']);
|
||||
// => 'fred says hello'
|
||||
```
|
||||
|
||||
## Lodash 兼容性
|
||||
|
||||
从 `es-toolkit/compat` 中导入 `spread` 以实现与 lodash 的完全兼容。
|
||||
|
||||
- `spread` 当给定的 `startIndex` 小于 0 或为 `NaN` 时将其视为 `0`。
|
||||
- `spread` 接受分数值的 `startIndex`,但将其强制转换为整数。
|
||||
|
||||
```typescript
|
||||
import { spread } from 'es-toolkit/compat';
|
||||
|
||||
function fn(a: unknown, b: unknown, c: unknown) {
|
||||
return Array.from(arguments);
|
||||
}
|
||||
|
||||
spread(fn, -1)([1, 2]); // Returns [1, 2]
|
||||
spread(fn, NaN)([1, 2]); // Returns [1, 2]
|
||||
spread(fn, 'a')([1, 2]); // Returns [1, 2]
|
||||
spread(fn, 1.6)(1, [2, 3]); // Returns [1, 2, 3]
|
||||
```
|
59
src/compat/function/spread.spec.ts
Normal file
59
src/compat/function/spread.spec.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { spread } from './spread';
|
||||
|
||||
describe('spread', () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function fn(_a: unknown, _b: unknown, _c: unknown) {
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
return Array.from(arguments);
|
||||
}
|
||||
|
||||
it('should spread arguments to `func`', () => {
|
||||
const spreadFn = spread(fn);
|
||||
const expected = [1, 2];
|
||||
|
||||
expect(spreadFn([1, 2])).toEqual(expected);
|
||||
expect(spreadFn([1, 2], 3)).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should accept a falsey `array`', () => {
|
||||
const falsey = [null, undefined, false, 0, NaN, ''];
|
||||
const spreadFn = spread(() => true);
|
||||
const expected = falsey.map(() => true);
|
||||
|
||||
const actual = falsey.map(array => {
|
||||
return spreadFn(array);
|
||||
});
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should work with `startIndex`', () => {
|
||||
const spreadFn = spread(fn, 1);
|
||||
const expected = [1, 2, 3];
|
||||
|
||||
expect(spreadFn(1, [2, 3])).toEqual(expected);
|
||||
expect(spreadFn(1, [2, 3], 4)).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should treat `start` as `0` for negative or `NaN` values', () => {
|
||||
const values = [-1, NaN, 'a'];
|
||||
const expected = values.map(() => [1, 2]);
|
||||
|
||||
const actual = values.map(value => {
|
||||
// @ts-expect-error - spreadFn is not being called with the correct arguments
|
||||
const spreadFn = spread(fn, value);
|
||||
return spreadFn([1, 2]);
|
||||
});
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should coerce `startIndex` to an integer', () => {
|
||||
const spreadFn = spread(fn, 1.6);
|
||||
const expected = [1, 2, 3];
|
||||
|
||||
expect(spreadFn(1, [2, 3])).toEqual(expected);
|
||||
expect(spreadFn(1, [2, 3], 4)).toEqual(expected);
|
||||
});
|
||||
});
|
22
src/compat/function/spread.ts
Normal file
22
src/compat/function/spread.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { spread as spreadToolkit } from '../../function/spread.ts';
|
||||
|
||||
/**
|
||||
* Creates a function that invokes `func` with the `this` binding of the create function and an array of arguments much like `Function#apply`.
|
||||
*
|
||||
* @template F The type of the function to spread arguments over.
|
||||
* @param {F} func The function to spread arguments over.
|
||||
* @param {number} startIndex The start position of the spread.
|
||||
* @returns {(...args: any[]) => ReturnType<F>} A new function that invokes `func` with the spread arguments.
|
||||
*/
|
||||
export function spread<F extends (...args: any[]) => any>(
|
||||
func: F,
|
||||
startIndex: number = 0
|
||||
): (...args: any[]) => ReturnType<F> {
|
||||
startIndex = Number.parseInt(startIndex as any, 10);
|
||||
|
||||
if (Number.isNaN(startIndex) || startIndex < 0) {
|
||||
startIndex = 0;
|
||||
}
|
||||
|
||||
return spreadToolkit(func, startIndex);
|
||||
}
|
@ -42,6 +42,7 @@ export { indexOf } from './array/indexOf.ts';
|
||||
export { ary } from './function/ary.ts';
|
||||
export { bind } from './function/bind.ts';
|
||||
export { rest } from './function/rest.ts';
|
||||
export { spread } from './function/spread.ts';
|
||||
|
||||
export { get } from './object/get.ts';
|
||||
export { set } from './object/set.ts';
|
||||
|
@ -11,3 +11,4 @@ export { unary } from './unary.ts';
|
||||
export { partial } from './partial.ts';
|
||||
export { partialRight } from './partialRight.ts';
|
||||
export { rest } from './rest.ts';
|
||||
export { spread } from './spread.ts';
|
||||
|
38
src/function/spread.spec.ts
Normal file
38
src/function/spread.spec.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { spread } from './spread';
|
||||
|
||||
describe('spread', () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
function fn(_a: unknown, _b: unknown, _c: unknown) {
|
||||
// eslint-disable-next-line prefer-rest-params
|
||||
return Array.from(arguments);
|
||||
}
|
||||
|
||||
it('should spread arguments to `func`', () => {
|
||||
const spreadFn = spread(fn);
|
||||
const expected = [1, 2];
|
||||
|
||||
expect(spreadFn([1, 2])).toEqual(expected);
|
||||
expect(spreadFn([1, 2], 3)).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should accept a falsey `array`', () => {
|
||||
const falsey = [null, undefined, false, 0, NaN, ''];
|
||||
const spreadFn = spread(() => true);
|
||||
const expected = falsey.map(() => true);
|
||||
|
||||
const actual = falsey.map(array => {
|
||||
return spreadFn(array);
|
||||
});
|
||||
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should work with `startIndex`', () => {
|
||||
const spreadFn = spread(fn, 1);
|
||||
const expected = [1, 2, 3];
|
||||
|
||||
expect(spreadFn(1, [2, 3])).toEqual(expected);
|
||||
expect(spreadFn(1, [2, 3], 4)).toEqual(expected);
|
||||
});
|
||||
});
|
22
src/function/spread.ts
Normal file
22
src/function/spread.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Creates a function that invokes `func` with the `this` binding of the create function and an array of arguments much like `Function#apply`.
|
||||
*
|
||||
* @template F The type of the function to spread arguments over.
|
||||
* @param {F} func The function to spread arguments over.
|
||||
* @param {number} startIndex The start position of the spread.
|
||||
* @returns {(...args: any[]) => ReturnType<F>} A new function that invokes `func` with the spread arguments.
|
||||
*/
|
||||
export function spread<F extends (...args: any[]) => any>(
|
||||
func: F,
|
||||
startIndex: number = 0
|
||||
): (...args: any[]) => ReturnType<F> {
|
||||
return function (this: any, ...args: any[]) {
|
||||
const array = args[startIndex];
|
||||
const params = args.slice(0, startIndex);
|
||||
|
||||
if (array) {
|
||||
params.push(...array);
|
||||
}
|
||||
return func.apply(this, params);
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user