Comparing version 3.2.1 to 3.3.0
@@ -0,2 +1,14 @@ | ||
export declare const add: (a: number, b: number) => number; | ||
export declare const subtract: (a: number, b: number) => number; | ||
export declare const multiply: (a: number, b: number) => number; | ||
export declare const divide: (a: number, b: number) => number; | ||
/** | ||
* Always returns the same value supplied to it. | ||
*/ | ||
export declare const identity: <T>(value: T) => T; | ||
/** | ||
* Returns a function that always returns the same value supplied to it. | ||
*/ | ||
export declare const always: <T>(value: T) => () => T; | ||
/** | ||
* Minimum required cache interface. | ||
@@ -20,6 +32,2 @@ */ | ||
/** | ||
* Always returns the same value that was used as the argument. | ||
*/ | ||
export declare function identity<T>(value: T): T; | ||
/** | ||
* Return a function that fetches `key` from its operand. | ||
@@ -34,8 +42,56 @@ */ | ||
export declare function invoke<K extends string | number | symbol, A extends any[]>(key: K, ...args: A): <T extends Record<K, (...args: A) => any>>(obj: T) => InvokeResult<T[K], A>; | ||
export interface ThrottleOptions { | ||
leading?: boolean; | ||
trailing?: boolean; | ||
debounce?: boolean; | ||
} | ||
/** | ||
* Wrap a function to rate-limit the function executions to once every `ms` milliseconds. | ||
*/ | ||
export declare function throttle(fn: () => void, ms: number, leading?: boolean): (() => void) & { | ||
export declare function throttle<T extends any[]>(fn: (...args: T) => void, ms: number, { leading, trailing, debounce }?: ThrottleOptions): ((...args: T) => void) & { | ||
flush: () => void; | ||
clear: () => void; | ||
}; | ||
/** | ||
* Given a `fn`, return a wrapper that accepts an array of `fn` arguments. | ||
*/ | ||
export declare function spread<T extends any[], R>(fn: (...args: T) => R): (args: T) => R; | ||
/** | ||
* Flip a binary `fn` argument order. | ||
*/ | ||
export declare function flip<T1, T2, R>(fn: (arg1: T1, arg2: T2) => R): (arg2: T2, arg1: T1) => R; | ||
/** | ||
* Returns a partially applied `fn` with the supplied arguments. | ||
*/ | ||
export declare function partial<U extends any[], R>(fn: (...args: U) => R): (...args: U) => R; | ||
export declare function partial<T1, U extends any[], R>(fn: (arg1: T1, ...args: U) => R, arg1: T1): (...args: U) => R; | ||
export declare function partial<T1, T2, U extends any[], R>(fn: (arg1: T1, arg2: T2, ...args: U) => R, arg1: T1, arg2: T2): (...args: U) => R; | ||
export declare function partial<T1, T2, T3, U extends any[], R>(fn: (arg1: T1, arg2: T2, arg3: T3, ...args: U) => R, arg1: T1, arg2: T2, arg3: T3): (...args: U) => R; | ||
export declare function partial<T1, T2, T3, T4, U extends any[], R>(fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, ...args: U) => R, arg1: T1, arg2: T2, arg3: T3, arg4: T4): (...args: U) => R; | ||
export declare function partial<T1, T2, T3, T4, T5, U extends any[], R>(fn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, ...args: U) => R, arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5): (...args: U) => R; | ||
/** | ||
* Left-to-right function composition. | ||
*/ | ||
export declare function sequence<T1, T2>(fn1: (arg: T1) => T2): (arg: T1) => T2; | ||
export declare function sequence<T1, T2, T3>(fn1: (arg: T1) => T2, fn2: (arg: T2) => T3): (arg: T1) => T3; | ||
export declare function sequence<T1, T2, T3, T4>(fn1: (arg: T1) => T2, fn2: (arg: T2) => T3, fn3: (arg: T3) => T4): (arg: T1) => T4; | ||
export declare function sequence<T1, T2, T3, T4, T5>(fn1: (arg: T1) => T2, fn2: (arg: T2) => T3, fn3: (arg: T3) => T4, fn4: (arg: T4) => T5): (arg: T1) => T5; | ||
export declare function sequence<T1, T2, T3, T4, T5, T6>(fn1: (arg: T1) => T2, fn2: (arg: T2) => T3, fn3: (arg: T3) => T4, fn4: (arg: T4) => T5, fn5: (arg: T5) => T6): (arg: T1) => T6; | ||
/** | ||
* Right-to-left function composition. | ||
*/ | ||
export declare function compose<T1, T2>(fn1: (arg: T1) => T2): (arg: T1) => T2; | ||
export declare function compose<T1, T2, T3>(fn2: (arg: T2) => T3, fn1: (arg: T1) => T2): (arg: T1) => T3; | ||
export declare function compose<T1, T2, T3, T4>(fn3: (arg: T3) => T4, fn2: (arg: T2) => T3, fn1: (arg: T1) => T2): (arg: T1) => T4; | ||
export declare function compose<T1, T2, T3, T4, T5>(fn4: (arg: T4) => T5, fn3: (arg: T3) => T4, fn2: (arg: T2) => T3, fn1: (arg: T1) => T2): (arg: T1) => T5; | ||
export declare function compose<T1, T2, T3, T4, T5, T6>(fn5: (arg: T5) => T6, fn4: (arg: T4) => T5, fn3: (arg: T3) => T4, fn2: (arg: T2) => T3, fn1: (arg: T1) => T2): (arg: T1) => T6; | ||
/** | ||
* Fix the number of receivable arguments in `origFn` to `n`. | ||
*/ | ||
export declare function nary<U extends any[], R>(n: 0, origFn: () => R): () => R; | ||
export declare function nary<T1, U extends any[], R>(n: 1, origFn: (arg1: T1) => R): (arg1: T1) => R; | ||
export declare function nary<T1, T2, U extends any[], R>(n: 2, origFn: (arg1: T1, arg2: T2) => R): (arg1: T1, arg2: T2) => R; | ||
export declare function nary<T1, T2, T3, U extends any[], R>(n: 3, origFn: (arg1: T1, arg2: T2, arg3: T3) => R): (arg1: T1, arg2: T2, arg3: T3) => R; | ||
export declare function nary<T1, T2, T3, T4, U extends any[], R>(n: 4, origFn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => R): (arg1: T1, arg2: T2, arg3: T3, arg4: T4) => R; | ||
export declare function nary<T1, T2, T3, T4, T5, U extends any[], R>(n: 5, origFn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) => R): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5) => R; | ||
export declare function nary<T1, T2, T3, T4, T5, T6, U extends any[], R>(n: 6, origFn: (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6) => R): (arg1: T1, arg2: T2, arg3: T3, arg4: T4, arg5: T5, arg6: T6) => R; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.add = (a, b) => a + b; | ||
exports.subtract = (a, b) => a - b; | ||
exports.multiply = (a, b) => a * b; | ||
exports.divide = (a, b) => a / b; | ||
/** | ||
* Always returns the same value supplied to it. | ||
*/ | ||
exports.identity = (value) => value; | ||
/** | ||
* Returns a function that always returns the same value supplied to it. | ||
*/ | ||
exports.always = (value) => () => value; | ||
/** | ||
* Optimize a function to speed up consecutive calls by caching the result of | ||
@@ -34,9 +46,2 @@ * calls with identical input arguments. The cache can be overrriden to | ||
/** | ||
* Always returns the same value that was used as the argument. | ||
*/ | ||
function identity(value) { | ||
return value; | ||
} | ||
exports.identity = identity; | ||
/** | ||
* Return a function that fetches `key` from its operand. | ||
@@ -59,41 +64,99 @@ */ | ||
*/ | ||
function throttle(fn, ms, leading = true) { | ||
let pending = false; | ||
function throttle(fn, ms, { leading = true, trailing = true, debounce = false } = {}) { | ||
let pending; | ||
let timeout = undefined; | ||
// Clear timeout. | ||
function clear() { | ||
pending = false; | ||
clearTimeout(timeout); | ||
timeout = undefined; | ||
pending = undefined; | ||
} | ||
// Wait for the next interval. | ||
function wait() { | ||
timeout = setTimeout(_next, ms); | ||
} | ||
// Invoke the function in "pending" state. | ||
function flush() { | ||
if (pending) | ||
return _next(); | ||
clearTimeout(timeout); | ||
return next(); | ||
} | ||
// Clear timeout or flush next function call. | ||
function _next() { | ||
// When no pending, remove `timeout`. | ||
if (!pending) { | ||
timeout = undefined; | ||
// Execute `fn` and increment timeout every loop. | ||
function next() { | ||
timeout = undefined; | ||
if (!pending) | ||
return; | ||
} | ||
clear(); // Clear existing timeout. | ||
fn(); // Execute pending function. | ||
wait(); // Start new interval. | ||
const args = pending; | ||
pending = undefined; | ||
if (trailing) | ||
fn(...args); | ||
timeout = setTimeout(next, ms); | ||
} | ||
return Object.assign(() => { | ||
pending = true; | ||
// Throttled `fn` wrapper. | ||
function throttled(...args) { | ||
pending = args; | ||
if (timeout === undefined) { | ||
if (leading === true) | ||
return _next(); | ||
return wait(); | ||
if (leading) { | ||
pending = undefined; | ||
fn(...args); | ||
} | ||
timeout = setTimeout(next, ms); | ||
return; | ||
} | ||
}, { flush, clear }); | ||
if (debounce) { | ||
clearTimeout(timeout); | ||
timeout = setTimeout(next, ms); | ||
return; | ||
} | ||
} | ||
return Object.assign(throttled, { flush, clear }); | ||
} | ||
exports.throttle = throttle; | ||
/** | ||
* Given a `fn`, return a wrapper that accepts an array of `fn` arguments. | ||
*/ | ||
function spread(fn) { | ||
return (args) => fn(...args); | ||
} | ||
exports.spread = spread; | ||
/** | ||
* Flip a binary `fn` argument order. | ||
*/ | ||
function flip(fn) { | ||
return (arg2, arg1) => fn(arg1, arg2); | ||
} | ||
exports.flip = flip; | ||
function partial(fn, ...args1) { | ||
return (...args2) => fn(...args1, ...args2); | ||
} | ||
exports.partial = partial; | ||
const SEQUENCE_FNS = Object.create(null); | ||
function sequence(...fns) { | ||
const n = fns.length; | ||
let fn = SEQUENCE_FNS[n]; | ||
if (!fn) { | ||
const params = fns.map((_, i) => `_${i}`); | ||
const seq = params.reduce((x, p) => `${p}(${x})`, "x"); | ||
fn = SEQUENCE_FNS[n] = new Function(...params, `return function sequence${n}(x) { return ${seq}; }`); | ||
} | ||
return fn(...fns); | ||
} | ||
exports.sequence = sequence; | ||
const COMPOSE_FNS = Object.create(null); | ||
function compose(...fns) { | ||
const n = fns.length; | ||
let fn = COMPOSE_FNS[n]; | ||
if (!fn) { | ||
const params = fns.map((_, i) => `_${i}`); | ||
const seq = params.reduceRight((x, p) => `${p}(${x})`, "x"); | ||
fn = COMPOSE_FNS[n] = new Function(...params, `return function compose${n}(x) { return ${seq}; }`); | ||
} | ||
return fn(...fns); | ||
} | ||
exports.compose = compose; | ||
const NARY_FNS = Object.create(null); | ||
function nary(n, origFn) { | ||
let fn = NARY_FNS[n]; | ||
if (!fn) { | ||
const args = Array.from({ length: n }, (_, i) => `_${i}`).join(", "); | ||
fn = NARY_FNS[n] = new Function("origFn", `return function nary${n}(${args}) { return origFn(${args}); }`); | ||
} | ||
return fn(origFn); | ||
} | ||
exports.nary = nary; | ||
//# sourceMappingURL=index.js.map |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const functools = require("./index"); | ||
describe('functools', () => { | ||
describe('identity', () => { | ||
it('should return the input', () => { | ||
describe("functools", () => { | ||
describe("identity", () => { | ||
it("should return the input", () => { | ||
expect(functools.identity(42)).toEqual(42); | ||
}); | ||
}); | ||
describe('memoize', () => { | ||
it('should cache return values by input argument', () => { | ||
describe("always", () => { | ||
it("should return a function that returns the input", () => { | ||
const fn = functools.always(42); | ||
expect(fn()).toEqual(42); | ||
}); | ||
}); | ||
describe("memoize", () => { | ||
it("should cache return values by input argument", () => { | ||
let i = 0; | ||
const fn = functools.memoize(() => ++i); | ||
expect(fn('foo')).toEqual(1); | ||
expect(fn('foo')).toEqual(1); | ||
expect(fn('bar')).toEqual(2); | ||
expect(fn('bar')).toEqual(2); | ||
expect(fn("foo")).toEqual(1); | ||
expect(fn("foo")).toEqual(1); | ||
expect(fn("bar")).toEqual(2); | ||
expect(fn("bar")).toEqual(2); | ||
}); | ||
}); | ||
describe('memoize0', () => { | ||
it('should memoize a zero-length function', () => { | ||
describe("memoize0", () => { | ||
it("should memoize a zero-length function", () => { | ||
let i = 0; | ||
@@ -28,13 +34,13 @@ const fn = functools.memoize0(() => ++i); | ||
}); | ||
describe('prop', () => { | ||
const getter = functools.prop('foo'); | ||
describe("prop", () => { | ||
const getter = functools.prop("foo"); | ||
expect(getter({ foo: 123 })).toEqual(123); | ||
expect(getter({})).toEqual(undefined); | ||
}); | ||
describe('invoke', () => { | ||
expect(functools.invoke('foo')({ foo: () => 123 })).toEqual(123); | ||
expect(functools.invoke('add', 3, 7)({ add: (a, b) => a + b })).toEqual(10); | ||
describe("invoke", () => { | ||
expect(functools.invoke("foo")({ foo: () => 123 })).toEqual(123); | ||
expect(functools.invoke("add", 3, 7)({ add: (a, b) => a + b })).toEqual(10); | ||
}); | ||
describe('throttle', () => { | ||
it('should throttle function calls', (done) => { | ||
describe("throttle", () => { | ||
it("should throttle function calls", done => { | ||
let i = 0; | ||
@@ -47,20 +53,21 @@ const fn = functools.throttle(() => ++i, 100); | ||
expect(i).toEqual(1); | ||
setTimeout(() => { | ||
fn(); // Works after timeout. | ||
expect(i).toEqual(2); | ||
return done(); | ||
}, 100); | ||
}, 10); | ||
setTimeout(() => { | ||
fn(); // Works after timeout. | ||
expect(i).toEqual(2); | ||
return done(); | ||
}, 100); | ||
}); | ||
it('should throttle function calls without leading call', (done) => { | ||
it("should throttle function calls without leading call", done => { | ||
let i = 0; | ||
const fn = functools.throttle(() => ++i, 100, false); | ||
const fn = functools.throttle(() => ++i, 100, { leading: false }); | ||
fn(); // Leading invoke disabled. | ||
expect(i).toEqual(0); | ||
setTimeout(() => fn(), 50); // Noop. | ||
setTimeout(() => { | ||
expect(i).toEqual(1); | ||
return done(); | ||
}, 200); | ||
}, 110); | ||
}); | ||
it('should flush the function call', (done) => { | ||
it("should flush the function call", done => { | ||
let i = 0; | ||
@@ -77,4 +84,4 @@ const fn = functools.throttle(() => ++i, 100); | ||
expect(i).toEqual(2); | ||
fn(); // Enqueue `fn` after last flush (no leading since has run). | ||
expect(i).toEqual(2); | ||
fn(); // Enqueue `fn` after last flush. | ||
expect(i).toEqual(3); | ||
setTimeout(() => { | ||
@@ -85,4 +92,67 @@ expect(i).toEqual(3); | ||
}); | ||
it("should skip trailing calls", done => { | ||
let i = 0; | ||
const fn = functools.throttle(() => ++i, 100, { | ||
trailing: false | ||
}); | ||
fn(); | ||
expect(i).toEqual(1); | ||
// Tracks `pending` for first "event loop". | ||
setTimeout(() => fn(), 20); | ||
setTimeout(() => fn(), 40); | ||
setTimeout(() => fn(), 60); | ||
setTimeout(() => fn(), 80); | ||
// Nothing happened between loops with `lagging` set. | ||
setTimeout(() => expect(i).toEqual(1), 150); | ||
setTimeout(() => { | ||
expect(i).toEqual(1); // Executed the second timeout. | ||
return done(); | ||
}, 210); | ||
}); | ||
it("should debounce function execution", done => { | ||
let i = 0; | ||
const fn = functools.throttle(() => ++i, 100, { | ||
leading: false, | ||
debounce: true | ||
}); | ||
fn(); | ||
expect(i).toEqual(0); | ||
// Tracks `pending` for first "event loop". | ||
setTimeout(() => fn(), 20); | ||
setTimeout(() => fn(), 40); | ||
setTimeout(() => fn(), 60); | ||
setTimeout(() => fn(), 80); | ||
// Nothing happened between loops with `debounce` set. | ||
setTimeout(() => expect(i).toEqual(0), 130); | ||
setTimeout(() => { | ||
expect(i).toEqual(1); // Executed after debounce. | ||
return done(); | ||
}, 190); | ||
}); | ||
}); | ||
describe("spread", () => { | ||
it("should spread function arguments", () => { | ||
const fn = functools.spread(functools.add); | ||
expect(fn([1, 2])).toEqual(3); | ||
}); | ||
}); | ||
describe("compose", () => { | ||
it("should compose a list of functions", () => { | ||
const fn = functools.compose(functools.partial(functools.multiply, 5), functools.partial(functools.flip(functools.subtract), 3)); | ||
expect(fn(10)).toEqual(35); | ||
}); | ||
}); | ||
describe("sequence", () => { | ||
it("should sequence a list of functions", () => { | ||
const fn = functools.sequence(functools.partial(functools.flip(functools.divide), 5), functools.partial(functools.add, 3)); | ||
expect(fn(10)).toEqual(5); | ||
}); | ||
}); | ||
describe("nary", () => { | ||
it("should restrict function arity", () => { | ||
const fn = functools.nary(1, parseInt); | ||
expect(["1", "2", "3"].map(fn)).toEqual([1, 2, 3]); | ||
}); | ||
}); | ||
}); | ||
//# sourceMappingURL=index.spec.js.map |
{ | ||
"name": "functools", | ||
"version": "3.2.1", | ||
"version": "3.3.0", | ||
"description": "Utilities for working with functions in JavaScript, with TypeScript", | ||
@@ -11,2 +11,4 @@ "main": "dist/index.js", | ||
"scripts": { | ||
"prettier": "prettier --write", | ||
"format": "npm run prettier -- \"{.,src/**}/*.{js,ts,json,md,yml,css}\"", | ||
"lint": "tslint \"src/**/*.ts\" --project tsconfig.json", | ||
@@ -16,3 +18,3 @@ "build": "rm -rf dist/ && tsc", | ||
"test": "npm run lint && npm run build && npm run specs", | ||
"prepublish": "npm run build" | ||
"prepare": "npm run build" | ||
}, | ||
@@ -56,9 +58,27 @@ "repository": { | ||
}, | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "lint-staged" | ||
} | ||
}, | ||
"lint-staged": { | ||
"*.{js,ts,json,md,yml,css}": [ | ||
"npm run prettier", | ||
"git add" | ||
] | ||
}, | ||
"publishConfig": { | ||
"access": "public" | ||
}, | ||
"devDependencies": { | ||
"@types/jest": "^23.3.1", | ||
"@types/node": "^10.1.2", | ||
"husky": "^1.3.1", | ||
"jest": "^23.5.0", | ||
"lint-staged": "^8.1.0", | ||
"prettier": "^1.16.1", | ||
"rimraf": "^2.6.2", | ||
"ts-jest": "^23.1.4", | ||
"tslint": "^5.11.0", | ||
"tslint-config-prettier": "^1.17.0", | ||
"tslint-config-standard": "^8.0.0", | ||
@@ -65,0 +85,0 @@ "typescript": "^3.0.3" |
@@ -22,8 +22,16 @@ # Functools | ||
Always returns the same value that was used as the argument. | ||
Always returns the same value supplied to it. | ||
```js | ||
identity(42) //=> 42 | ||
identity(42); //=> 42 | ||
``` | ||
### `always<T>(arg: T) => () => T` | ||
Returns a function that always returns the same value supplied to it. | ||
```js | ||
identity(42); //=> 42 | ||
``` | ||
### `memoize<T, U>(fn: (x: T) => U, cache?: Cache) => (x: T) => U` | ||
@@ -34,13 +42,13 @@ | ||
```js | ||
let i = 0 | ||
const fn = memoize(() => ++i) | ||
let i = 0; | ||
const fn = memoize(() => ++i); | ||
fn('foo') //=> 1 | ||
fn('foo') //=> 1 | ||
fn("foo"); //=> 1 | ||
fn("foo"); //=> 1 | ||
fn('bar') //=> 2 | ||
fn('bar') //=> 2 | ||
fn("bar"); //=> 2 | ||
fn("bar"); //=> 2 | ||
``` | ||
See also: `memoize0` for zero-length function arguments. | ||
**See also:** `memoize0` for zero-length function arguments. | ||
@@ -52,3 +60,3 @@ ### `prop<K>(key: K) => (obj: T) => T[K]` | ||
```js | ||
prop('foo')({ foo: 123 }) //=> 123 | ||
prop("foo")({ foo: 123 }); //=> 123 | ||
``` | ||
@@ -61,6 +69,6 @@ | ||
```js | ||
invoke('add', 5, 5)({ add: (a, b) => a + b }) //=> 10 | ||
invoke("add", 5, 5)({ add: (a, b) => a + b }); //=> 10 | ||
``` | ||
### `throttle(fn: () => void, ms: number, leading = true) => () => void` | ||
### `throttle<T>(fn: (...args: T) => void, ms: number, { leading, trailing, debounce }) => (...args: T) => void` | ||
@@ -85,2 +93,53 @@ Wrap a function to rate-limit the function executions to once every `ms` milliseconds. | ||
### `spread<T, R>(fn: (...args: T) => R) => (args: T) => R` | ||
Given a `fn`, return a wrapper that accepts an array of `fn` arguments. | ||
```js | ||
Promise.all([1, 2, 3]).then(spread(add)); | ||
``` | ||
### `flip<T1, T2, R>(fn: (arg1: T1, arg2: T2) => R) => (arg2: T2, arg1: T1) => R` | ||
Flip a binary `fn` argument order. | ||
```js | ||
flip(subtract)(5, 10); //=> 5 | ||
``` | ||
### `partial<T, U, R>(fn: (...args1: T, ...args2: U) => R) => (...args: U) => R` | ||
Returns a partially applied `fn` with the supplied arguments. | ||
```js | ||
partial(subtract, 10)(5); //=> 5 | ||
``` | ||
### `sequence<T>(...fns: Array<(input: T) => T>) => (input: T) => T` | ||
Left-to-right function composition. | ||
```js | ||
sequence(partial(add, 10), partial(multiply, 5))(5); //=> 75 | ||
``` | ||
### `compose<T>(...fns: Array<(input: T) => T>) => (input: T) => T` | ||
Right-to-left function composition. | ||
```js | ||
compose( | ||
partial(add, 10), | ||
partial(multiply, 5) | ||
)(5); //=> 35 | ||
``` | ||
### `nary<T, R>(n: number, fn: (...args: T) => R) => (...args: T) => R` | ||
Fix the number of receivable arguments in `origFn` to `n`. | ||
```js | ||
["1", "2", "3"].map(nary(1, fn)); //=> [1, 2, 3] | ||
``` | ||
## TypeScript | ||
@@ -87,0 +146,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
48668
408
147
12
1