Repository: char0n/ramda-adjunct
Pull request #1086 (2019-10-03 11:46:22)

feat(object): add pathOrLazy
https://github.com/char0n/ramda-adjunct/issues/1084


Patch:
diff --git a/package-lock.json b/package-lock.json
index 5570638eb0..13c8a2e8ba 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "ramda-adjunct",
-  "version": "2.21.0",
+  "version": "2.22.0",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -4650,9 +4650,9 @@
       }
     },
     "eslint-config-prettier": {
-      "version": "6.3.0",
-      "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.3.0.tgz",
-      "integrity": "sha512-EWaGjlDAZRzVFveh2Jsglcere2KK5CJBhkNSa1xs3KfMUGdRiT7lG089eqPdvlzWHpAqaekubOsOMu8W8Yk71A==",
+      "version": "6.4.0",
+      "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-6.4.0.tgz",
+      "integrity": "sha512-YrKucoFdc7SEko5Sxe4r6ixqXPDP1tunGw91POeZTTRKItf/AMFYt/YLEQtZMkR2LVpAVhcAcZgcWpm1oGPW7w==",
       "dev": true,
       "requires": {
         "get-stdin": "^6.0.0"
diff --git a/package.json b/package.json
index d4dbd06ec9..50f183330e 100644
--- a/package.json
+++ b/package.json
@@ -42,7 +42,7 @@
     "functional"
   ],
   "sideEffects": false,
-  "version": "2.21.0",
+  "version": "2.22.0",
   "homepage": "https://github.com/char0n/ramda-adjunct",
   "license": "BSD-3-Clause",
   "repository": {
@@ -147,7 +147,7 @@
     "dtslint": "0.9.7",
     "eslint": "6.5.1",
     "eslint-config-airbnb-base": "=14.0.0",
-    "eslint-config-prettier": "6.3.0",
+    "eslint-config-prettier": "6.4.0",
     "eslint-plugin-import": "2.18.2",
     "eslint-plugin-mocha": "6.1.1",
     "eslint-plugin-prettier": "3.1.1",
diff --git a/src/anyP.js b/src/anyP.js
new file mode 100644
index 0000000000..4777572518
--- /dev/null
+++ b/src/anyP.js
@@ -0,0 +1,34 @@
+import { bind } from 'ramda';
+import curry1 from 'ramda/src/internal/_curry1';
+
+import isFunction from './isFunction';
+import polyfill, { AggregatedError } from './internal/polyfills/Promise.any';
+
+export const anyPPolyfill = curry1(polyfill);
+export { AggregatedError };
+
+/**
+ * Returns a promise that is fulfilled by the first given promise to be fulfilled,
+ * or rejected with an array of rejection reasons if all of the given promises are rejected.
+ *
+ * @func anyP
+ * @memberOf RA
+ * @category Function
+ * @since {@link https://char0n.github.io/ramda-adjunct/2.22.0|v2.22.0}
+ * @sig [Promise a] -> Promise a
+ * @param {Iterable.<*>} iterable An iterable object such as an Array or String
+ * @return {Promise} Returns a promise that is fulfilled by the first given promise to be fulfilled,
+ * or rejected with an array of rejection reasons if all of the given promises are rejected.
+ * @example
+ *
+ * RA.anyP([
+ *   Promise.resolve(1),
+ *   2,
+ *   Promise.reject(3),
+ * ]); //=> Promise(1)
+ */
+const anyP = isFunction(Promise.any)
+  ? curry1(bind(Promise.any, Promise))
+  : anyPPolyfill;
+
+export default anyP;
diff --git a/src/index.js b/src/index.js
index d9e895148d..79e14d6fa1 100644
--- a/src/index.js
+++ b/src/index.js
@@ -102,6 +102,7 @@ export { default as seq } from './seq';
 export { default as sequencing } from './seq';
 export { default as dispatch } from './dispatch';
 export { default as async } from './async';
+export { default as anyP } from './anyP';
 // List
 export { default as mapIndexed } from './mapIndexed';
 export { default as reduceIndexed } from './reduceIndexed';
@@ -144,6 +145,7 @@ export { default as mergePaths } from './mergePaths';
 export { default as mergeProp } from './mergeProp';
 export { default as mergePath } from './mergePath';
 export { default as omitBy } from './omitBy';
+export { default as pathOrLazy } from './pathOrLazy';
 export { default as viewOr } from './viewOr';
 export { default as hasPath } from './hasPath';
 export { default as spreadProp } from './spreadProp';
diff --git a/src/internal/polyfills/Promise.any.js b/src/internal/polyfills/Promise.any.js
new file mode 100644
index 0000000000..ab26552680
--- /dev/null
+++ b/src/internal/polyfills/Promise.any.js
@@ -0,0 +1,35 @@
+import { map } from 'ramda';
+
+import resolveP from '../../resolveP';
+
+export class AggregatedError extends Error {
+  constructor(errors = [], message) {
+    super(message);
+
+    this.errors = errors;
+  }
+}
+
+const anyPolyfill = iterable => {
+  const exceptions = [];
+
+  return new Promise((resolve, reject) => {
+    const onReject = e => {
+      exceptions.push(e);
+
+      if (exceptions.length === iterable.length) {
+        reject(new AggregatedError(exceptions));
+      }
+    };
+
+    map(
+      p =>
+        resolveP(p)
+          .then(resolve)
+          .catch(onReject),
+      [...iterable]
+    );
+  });
+};
+
+export default anyPolyfill;
diff --git a/src/pathOrLazy.js b/src/pathOrLazy.js
new file mode 100644
index 0000000000..fdc403c521
--- /dev/null
+++ b/src/pathOrLazy.js
@@ -0,0 +1,30 @@
+import { curryN, identical, partial, pathOr, unary, when } from 'ramda';
+
+/**
+ * If the given, non-null object has a value at the given path, returns the value at that path.
+ * Otherwise returns the result of invoking the provided function with the object.
+ *
+ * @func pathOrLazy
+ * @memberOf RA
+ * @since {@link https://char0n.github.io/ramda-adjunct/2.22.0|v2.22.0}
+ * @category Object
+ * @typedef Idx = String | Int
+ * @sig ({a} -> a) -> [Idx] -> {a} -> a
+ * @param {Function} defaultFn The function that will return the default value.
+ * @param {Array} path The path to use.
+ * @param {Object} obj The object to retrieve the nested property from.
+ * @return {*} The data at `path` of the supplied object or the default value.
+ * @example
+ *
+ * RA.pathOrLazy(() => 'N/A', ['a', 'b'], {a: {b: 2}}); //=> 2
+ * RA.pathOrLazy(() => 'N/A', ['a', 'b'], {c: {b: 2}}); //=> "N/A"
+ */
+const pathOrLazy = curryN(3, function pathOrLazy(defaultFn, path, obj) {
+  return when(
+    identical(defaultFn),
+    partial(unary(defaultFn), [obj]),
+    pathOr(defaultFn, path, obj)
+  );
+});
+
+export default pathOrLazy;
diff --git a/types/index.d.ts b/types/index.d.ts
index 667990f5f9..e1e4f8e98e 100644
--- a/types/index.d.ts
+++ b/types/index.d.ts
@@ -35,6 +35,8 @@ declare namespace RamdaAdjunct {
 
     type Variadic<T1, T2> = (...args: T1[]) => T2;
 
+    type Pred = (...a: any[]) => boolean;
+
     interface Dictionary<T> { [key: string]: T; }
 
     type DictPred<T> = (value: T, key: string) => boolean;
@@ -445,6 +447,26 @@ declare namespace RamdaAdjunct {
         paths(ps: Array<Array<string | number>>, obj: object): any[];
         paths(ps: Array<Array<string | number>>): (obj: object) => any[];
 
+        /**
+         * If the given, non-null object has a value at the given path, returns the value at that path.
+         * Otherwise returns the result of invoking the provided function with the object.
+         */
+        pathOrLazy<T>(
+            defaultValueFn: () => T,
+            path: Array<number | string>,
+            obj: object
+        ): T;
+        pathOrLazy<T>(
+            defaultValueFn: () => T,
+            path: Array<number | string>
+        ): (obj: object) => T;
+        pathOrLazy<T>(
+            defaultValueFn: () => T
+        ): {
+            (path: Array<number | string>, obj: object): T;
+            (path: Array<number | string>): (obj: object) => T;
+        };
+
         /**
          * "lifts" a function to be the specified arity, so that it may "map over" objects that satisfy
          * the Apply spec of fantasy land.
@@ -789,6 +811,12 @@ declare namespace RamdaAdjunct {
         */
         allSettledP<T>(iterable: Iterable<T>): Promise<Array<SettledPromise<T>>>;
 
+        /**
+         * Returns a promise that is fulfilled by the first given promise to be fulfilled,
+         * or rejected with an array of rejection reasons if all of the given promises are rejected.
+         */
+        anyP<T>(iterable: Iterable<T>): Promise<T>;
+
         /**
          * Composable shortcut for `Promise.resolve`.
          *
@@ -1043,8 +1071,8 @@ declare namespace RamdaAdjunct {
          * more arguments than functions, any remaining arguments are passed in to the combining
          * predicate untouched.
          */
-        argsPass(combiningPredicate: Function, predicates: Function[]): Function;
-        argsPass(combiningPredicate: Function): (predicates: Function[]) => Function;
+        argsPass<T>(combiningPredicate: (fn: (a: T) => boolean) => (list: T[]) => boolean, predicates: Pred[]): Pred;
+        argsPass<T>(combiningPredicate: (fn: (a: T) => boolean) => (list: T[]) => boolean): (predicates: Pred[]) => Pred;
 
         /**
          * Accepts a function with any arity and returns a function with arity of zero.
@@ -1161,7 +1189,6 @@ declare namespace RamdaAdjunct {
          * Escapes the RegExp special characters.
          */
         escapeRegExp(val: string): string;
-        escapeRegExp<T>(val: T): T;
 
         /**
          * Divides two numbers, where the second number is divided by the first number.


Test patch:
diff --git a/test/anyP.js b/test/anyP.js
new file mode 100644
index 0000000000..f21c2ef4f7
--- /dev/null
+++ b/test/anyP.js
@@ -0,0 +1,191 @@
+import * as R from 'ramda';
+import { assert } from 'chai';
+
+import * as RA from '../src';
+import { anyPPolyfill, AggregatedError } from '../src/anyP';
+
+describe('anyP', function() {
+  context('given list of fulfilled promises', function() {
+    specify('should return first of fulfilled values', async function() {
+      const p1 = RA.resolveP(1);
+      const p2 = RA.resolveP(2);
+      const p3 = RA.resolveP(3);
+      const actual = [p1, p2, p3];
+      const expected = 1;
+
+      assert.strictEqual(await RA.anyP(actual), expected);
+    });
+  });
+
+  context('given list of rejected promises', function() {
+    specify(
+      'should fail with AggregatedError that contains a list of rejected reasons',
+      async function() {
+        const p1 = RA.rejectP(1);
+        const p2 = RA.rejectP(2);
+        const p3 = RA.rejectP(3);
+        const actual = [p1, p2, p3];
+        const expected = [1, 2, 3];
+
+        try {
+          await RA.anyP(actual);
+          throw new Error('resolving should fail');
+        } catch (e) {
+          assert.notStrictEqual(e.message, 'resolving should fail');
+          assert.instanceOf(e, AggregatedError);
+          assert.deepEqual(e.errors, expected);
+        }
+      }
+    );
+  });
+
+  context('given list of fulfilled and rejected promises', function() {
+    specify('should return first of fulfilled values', async function() {
+      const p1 = RA.rejectP(1);
+      const p2 = RA.resolveP(2);
+      const p3 = RA.rejectP(3);
+      const actual = [p1, p2, p3];
+      const expected = 2;
+
+      assert.strictEqual(await RA.anyP(actual), expected);
+    });
+  });
+
+  context('given list of values', function() {
+    specify('should return first of fulfilled values', async function() {
+      const v1 = 1;
+      const v2 = 2;
+      const v3 = 3;
+      const actual = [v1, v2, v3];
+      const expected = 1;
+
+      assert.strictEqual(await RA.anyP(actual), expected);
+    });
+  });
+
+  context('given list of values and promises', function() {
+    specify('should return first of fulfilled values', async function() {
+      const p1 = RA.resolveP(1);
+      const v2 = 2;
+      const p3 = RA.resolveP(3);
+      const actual = [p1, v2, p3];
+      const expected = 1;
+
+      assert.strictEqual(await RA.anyP(actual), expected);
+    });
+  });
+
+  context('given large number of promises', function() {
+    specify('should handle them properly', async function() {
+      const list = R.range(1, 1000);
+      const actual = list.map(RA.resolveP);
+      const expected = list[0];
+
+      assert.strictEqual(await RA.anyP(actual), expected);
+    });
+  });
+
+  it('should support placeholder to specify "gaps"', async function() {
+    const anyP = RA.anyP(R.__);
+    const p1 = RA.resolveP(1);
+    const v2 = 2;
+    const p3 = RA.resolveP(3);
+    const actual = [p1, v2, p3];
+    const expected = 1;
+
+    assert.strictEqual(await anyP(actual), expected);
+  });
+
+  context('anyPPolyfill', function() {
+    context('given list of fulfilled promises', function() {
+      specify('should return first of fulfilled values', async function() {
+        const p1 = RA.resolveP(1);
+        const p2 = RA.resolveP(2);
+        const p3 = RA.resolveP(3);
+        const actual = [p1, p2, p3];
+        const expected = 1;
+
+        assert.strictEqual(await anyPPolyfill(actual), expected);
+      });
+    });
+
+    context('given list of rejected promises', function() {
+      specify(
+        'should fail with AggregatedError that contains a list of rejected reasons',
+        async function() {
+          const p1 = RA.rejectP(1);
+          const p2 = RA.rejectP(2);
+          const p3 = RA.rejectP(3);
+          const actual = [p1, p2, p3];
+          const expected = [1, 2, 3];
+
+          try {
+            await anyPPolyfill(actual);
+            throw new Error('resolving should fail');
+          } catch (e) {
+            assert.notStrictEqual(e.message, 'resolving should fail');
+            assert.instanceOf(e, AggregatedError);
+            assert.deepEqual(e.errors, expected);
+          }
+        }
+      );
+    });
+
+    context('given list of fulfilled and rejected promises', function() {
+      specify('should return first of fulfilled values', async function() {
+        const p1 = RA.rejectP(1);
+        const p2 = RA.resolveP(2);
+        const p3 = RA.rejectP(3);
+        const actual = [p1, p2, p3];
+        const expected = 2;
+
+        assert.strictEqual(await anyPPolyfill(actual), expected);
+      });
+    });
+
+    context('given list of values', function() {
+      specify('should return first of fulfilled values', async function() {
+        const v1 = 1;
+        const v2 = 2;
+        const v3 = 3;
+        const actual = [v1, v2, v3];
+        const expected = 1;
+
+        assert.strictEqual(await anyPPolyfill(actual), expected);
+      });
+    });
+
+    context('given list of values and promises', function() {
+      specify('should return first of fulfilled values', async function() {
+        const p1 = RA.resolveP(1);
+        const v2 = 2;
+        const p3 = RA.resolveP(3);
+        const actual = [p1, v2, p3];
+        const expected = 1;
+
+        assert.strictEqual(await anyPPolyfill(actual), expected);
+      });
+    });
+
+    context('given large number of promises', function() {
+      specify('should handle them properly', async function() {
+        const list = R.range(1, 1000);
+        const actual = list.map(RA.resolveP);
+        const expected = list[0];
+
+        assert.strictEqual(await anyPPolyfill(actual), expected);
+      });
+    });
+
+    it('should support placeholder to specify "gaps"', async function() {
+      const anyP = anyPPolyfill(R.__);
+      const p1 = RA.resolveP(1);
+      const v2 = 2;
+      const p3 = RA.resolveP(3);
+      const actual = [p1, v2, p3];
+      const expected = 1;
+
+      assert.strictEqual(await anyP(actual), expected);
+    });
+  });
+});
diff --git a/test/pathOrLazy.js b/test/pathOrLazy.js
new file mode 100644
index 0000000000..c919a87cb1
--- /dev/null
+++ b/test/pathOrLazy.js
@@ -0,0 +1,65 @@
+import sinon from 'sinon';
+
+import * as RA from '../src';
+import eq from './shared/eq';
+
+describe('pathOrLazy', function() {
+  const obj = {
+    a: { b: { c: 1, d: ['x', 'y', 'z'] } },
+    e: NaN,
+    f: null,
+    g: undefined,
+  };
+
+  context('given requested path exists', function() {
+    specify('should return value at path', function() {
+      eq(RA.pathOrLazy(() => {}, ['a', 'b', 'c'], obj), 1);
+    });
+
+    specify('should not invoke lazy function', function() {
+      const fn = sinon.spy();
+      RA.pathOrLazy(fn, ['a', 'b', 'c'], obj);
+      eq(fn.notCalled, true);
+    });
+  });
+
+  context('given requested path does not exist', function() {
+    context('and the value is missing', function() {
+      specify('should return default value', function() {
+        eq(RA.pathOrLazy(() => 7, ['nonexistent'], obj), 7);
+      });
+    });
+
+    context('and the value is NaN', function() {
+      specify('should return default value', function() {
+        eq(RA.pathOrLazy(() => 7, ['e'], obj), 7);
+      });
+    });
+
+    context('and the value is null', function() {
+      specify('should return default value', function() {
+        eq(RA.pathOrLazy(() => 7, ['f'], obj), 7);
+      });
+    });
+
+    context('and the value is undefined', function() {
+      specify('should return default value', function() {
+        eq(RA.pathOrLazy(() => 7, ['g'], obj), 7);
+      });
+    });
+
+    specify('should pass object to lazy function as only argument', function() {
+      const fn = sinon.spy();
+      RA.pathOrLazy(fn, ['nonexistent'], obj);
+      eq(fn.calledOnceWithExactly(obj), true);
+    });
+  });
+
+  it('should curry', function() {
+    const fn = () => 7;
+    eq(RA.pathOrLazy(fn, ['a', 'b', 'd', 1], obj), 'y');
+    eq(RA.pathOrLazy(fn)(['a', 'b', 'd', 1], obj), 'y');
+    eq(RA.pathOrLazy(fn, ['a', 'b', 'd', 1])(obj), 'y');
+    eq(RA.pathOrLazy(fn)(['a', 'b', 'd', 1])(obj), 'y');
+  });
+});
diff --git a/types/test/allEqual.ts b/types/test/allEqual.ts
index a7c1b244e2..9066cd6773 100644
--- a/types/test/allEqual.ts
+++ b/types/test/allEqual.ts
@@ -1,4 +1,12 @@
 import * as RA from 'ramda-adjunct';
 
+import { customIterable } from './helpers';
+
 RA.allEqual([1, 2, 3, 4]); // $ExpectType boolean
+RA.allEqual(['a', 'b', 'c', 'd']); // $ExpectType boolean
+RA.allEqual([1, 'b', 2, 'd']); // $ExpectType boolean
 RA.allEqual([]); // $ExpectType boolean
+
+RA.allEqual(1); // $ExpectError
+RA.allEqual({}); // $ExpectError
+RA.allEqual(customIterable); // $ExpectError
diff --git a/types/test/allEqualTo.ts b/types/test/allEqualTo.ts
index adf9999476..0ecee6a8da 100644
--- a/types/test/allEqualTo.ts
+++ b/types/test/allEqualTo.ts
@@ -1,6 +1,12 @@
 import * as RA from "ramda-adjunct";
 
+import { customIterable } from './helpers';
+
 RA.allEqualTo(1, [1, 2, 3, 4]); // $ExpectType boolean
 RA.allEqualTo(1, []); // $ExpectType boolean
 RA.allEqualTo(1)([1, 2, 3, 4]); // $ExpectType boolean
 RA.allEqualTo(1)([]); // $ExpectType boolean
+
+RA.allEqualTo(1, {}); // $ExpectError
+RA.allEqualTo(1, 'a'); // $ExpectError
+RA.allEqualTo(1, customIterable); // $ExpectError
diff --git a/types/test/allIdentical.ts b/types/test/allIdentical.ts
index 8c1dbb1283..05c14ca5b4 100644
--- a/types/test/allIdentical.ts
+++ b/types/test/allIdentical.ts
@@ -1,4 +1,10 @@
 import * as RA from "ramda-adjunct";
 
+import { customIterable } from './helpers';
+
 RA.allIdentical([1, 2, 3, 4]); // $ExpectType boolean
 RA.allIdentical([]); // $ExpectType boolean
+
+RA.allIdentical({}); // $ExpectError
+RA.allIdentical('a'); // $ExpectError
+RA.allIdentical(customIterable); // $ExpectError
diff --git a/types/test/allIdenticalTo.ts b/types/test/allIdenticalTo.ts
index afc213a29b..d140f05fec 100644
--- a/types/test/allIdenticalTo.ts
+++ b/types/test/allIdenticalTo.ts
@@ -1,4 +1,10 @@
 import * as RA from "ramda-adjunct";
 
+import { customIterable } from './helpers';
+
 RA.allIdenticalTo(1, [1, 2, 3, 4]); // $ExpectType boolean
 RA.allIdenticalTo(1, []); // $ExpectType boolean
+RA.allIdenticalTo(1)([]); // $ExpectType boolean
+
+RA.allIdenticalTo('a', [1, 2]); // $ExpectError
+RA.allIdenticalTo(1, customIterable); // $ExpectError
diff --git a/types/test/allP.ts b/types/test/allP.ts
index a479a8d71b..9d180261df 100644
--- a/types/test/allP.ts
+++ b/types/test/allP.ts
@@ -1,23 +1,13 @@
 import * as RA from 'ramda-adjunct';
 
-/**
- * Helpers.
- */
-
-const customIterable = {
-    *[Symbol.iterator](): IterableIterator<number> {
-        yield 1;
-        yield 2;
-        yield 3;
-    },
-};
-
-/**
- * Tests.
- */
+import { customIterable } from './helpers';
 
 RA.allP([1, 2]); // $ExpectType Promise<number[]>
 RA.allP(['a', 'b']); // $ExpectType Promise<string[]>
 RA.allP([1, 'a']); // $ExpectType Promise<(string | number)[]>
 RA.allP([]); // $ExpectType Promise<never[]>
 RA.allP(customIterable); // $ExpectType Promise<number[]>
+RA.allP('abc'); // $ExpectType Promise<string[]>
+
+RA.allP({}); // $ExpectError
+RA.allP(1); // $ExpectError
diff --git a/types/test/allSettledP.ts b/types/test/allSettledP.ts
index a9dbdd5d79..f29481b9b3 100644
--- a/types/test/allSettledP.ts
+++ b/types/test/allSettledP.ts
@@ -1,23 +1,13 @@
 import * as RA from 'ramda-adjunct';
 
-/**
- * Helpers.
- */
-
-const customIterable = {
-    *[Symbol.iterator](): IterableIterator<number> {
-        yield 1;
-        yield 2;
-        yield 3;
-    },
-};
-
-/**
- * Tests.
- */
+import { customIterable } from './helpers';
 
 RA.allSettledP([1, 2]); // $ExpectType Promise<SettledPromise<number>[]>
 RA.allSettledP(['a', 'b']); // $ExpectType Promise<SettledPromise<string>[]>
 RA.allSettledP([1, 'a']); // $ExpectType Promise<SettledPromise<string | number>[]>
 RA.allSettledP([]); // $ExpectType Promise<SettledPromise<never>[]>
 RA.allSettledP(customIterable); // $ExpectType Promise<SettledPromise<number>[]>
+RA.allSettledP('abc'); // $ExpectType Promise<SettledPromise<string>[]>
+
+RA.allSettledP({}); // $ExpectError
+RA.allSettledP(1); // $ExpectError
diff --git a/types/test/anyP.ts b/types/test/anyP.ts
new file mode 100644
index 0000000000..73d289df86
--- /dev/null
+++ b/types/test/anyP.ts
@@ -0,0 +1,12 @@
+import * as RA from 'ramda-adjunct';
+
+import { customIterable } from './helpers';
+
+RA.anyP([1, 2]); // $ExpectType Promise<number>
+RA.anyP(['a', 'b']); // $ExpectType Promise<string>
+RA.anyP([1, 'a']); // $ExpectType Promise<string | number>
+RA.anyP([]); // $ExpectType Promise<never>
+RA.anyP(customIterable); // $ExpectType Promise<number>
+
+RA.anyP({}); // $ExpectError
+RA.anyP(1); // $ExpectError
diff --git a/types/test/appendFlipped.ts b/types/test/appendFlipped.ts
new file mode 100644
index 0000000000..7a17ed09ed
--- /dev/null
+++ b/types/test/appendFlipped.ts
@@ -0,0 +1,16 @@
+import * as RA from 'ramda-adjunct';
+
+import { customIterable } from './helpers';
+
+RA.appendFlipped([1, 2], 3); // $ExpectType number[]
+RA.appendFlipped(['a', 'b'], 'c'); // $ExpectType string[]
+RA.appendFlipped(['a', 2], 3); // $ExpectType (string | number)[]
+RA.appendFlipped<number>([1, 2], 3); // $ExpectType number[]
+RA.appendFlipped<string>(['a', 'b'], 'c'); // $ExpectType string[]
+
+RA.appendFlipped<string>([1, 'b'], 'c'); // $ExpectError
+RA.appendFlipped<string>([1, 'b'])('c'); // $ExpectError
+RA.appendFlipped(1, 'c'); // $ExpectError
+RA.appendFlipped(1)('c'); // $ExpectError
+RA.appendFlipped(customIterable, 'c'); // $ExpectError
+RA.appendFlipped(customIterable)('c'); // $ExpectError
diff --git a/types/test/argsPass.ts b/types/test/argsPass.ts
new file mode 100644
index 0000000000..e59919ab5f
--- /dev/null
+++ b/types/test/argsPass.ts
@@ -0,0 +1,31 @@
+import * as RA from 'ramda-adjunct';
+
+/**
+ * Helpers.
+ */
+
+const combiningPredicate = <T>(predicateFn: Function) => (iterable: T[]): boolean =>
+    iterable.reduce((acc: boolean, val: T) => acc && predicateFn(val), true);
+
+const nonBoolPredicate = (val: string): string => val;
+
+const nonCurriedCombiningPredicate = <T>(predicateFn: Function, iterable: T[]): boolean =>
+    iterable.reduce((acc: boolean, val: T) => acc && predicateFn(val), true);
+
+/**
+ * Tests.
+ */
+
+RA.argsPass(combiningPredicate, [Boolean, Boolean])(1, 2); // $ExpectType boolean
+RA.argsPass(combiningPredicate)([Boolean, Boolean])(1, 2); // $ExpectType boolean
+RA.argsPass(combiningPredicate, [Boolean, Boolean])('a', 'b'); // $ExpectType boolean
+RA.argsPass(combiningPredicate)([Boolean, Boolean])('a', 'b'); // $ExpectType boolean
+RA.argsPass(combiningPredicate, [Boolean, Boolean])('a', 2); // $ExpectType boolean
+RA.argsPass(combiningPredicate)([Boolean, Boolean])('a', 2); // $ExpectType boolean
+RA.argsPass(combiningPredicate, [Boolean, Boolean])(); // $ExpectType boolean
+
+RA.argsPass(combiningPredicate)([Boolean, Boolean])(); // $ExpectType boolean
+
+RA.argsPass(nonCurriedCombiningPredicate, [Boolean, Boolean])(1, 2); // $ExpectError
+
+RA.argsPass(combiningPredicate, [nonBoolPredicate])(1, 2); // $ExpectError
diff --git a/types/test/divideNum.ts b/types/test/divideNum.ts
index 7404b32efe..6cbdbc0d13 100644
--- a/types/test/divideNum.ts
+++ b/types/test/divideNum.ts
@@ -11,3 +11,12 @@ RA.divideNum(2.0)(1.0); // $ExpectType number
 RA.divideNum(0)(2.0); // $ExpectType number
 RA.divideNum(0.0)(2.0); // $ExpectType number
 RA.divideNum(-0.0)(2.0); // $ExpectType number
+
+RA.divideNum('a', 'b'); // $ExpectError
+RA.divideNum('a')('b'); // $ExpectError
+RA.divideNum(1, undefined); // $ExpectError
+RA.divideNum(1)(undefined); // $ExpectError
+RA.divideNum(1, {}); // $ExpectError
+RA.divideNum(1)({}); // $ExpectError
+RA.divideNum({}, 1); // $ExpectError
+RA.divideNum({})(1); // $ExpectError
diff --git a/types/test/escapeRegExp.ts b/types/test/escapeRegExp.ts
index 7106e6ce24..dbe7f25797 100644
--- a/types/test/escapeRegExp.ts
+++ b/types/test/escapeRegExp.ts
@@ -1,8 +1,12 @@
 import * as RA from 'ramda-adjunct';
 
 RA.escapeRegExp('abc'); // $ExpectType string
-RA.escapeRegExp(1); // $ExpectType 1
-RA.escapeRegExp({}); // $ExpectType {}
-RA.escapeRegExp<number>(1); // $ExpectType number
-RA.escapeRegExp<null>(null); // $ExpectType null
-RA.escapeRegExp<undefined>(undefined); // $ExpectType undefined
+RA.escapeRegExp(''); // $ExpectType string
+
+// tslint:disable-next-line:no-construct
+RA.escapeRegExp(new String('')); // $ExpectError
+RA.escapeRegExp(1); // $ExpectError
+RA.escapeRegExp({}); // $ExpectError
+RA.escapeRegExp(null); // $ExpectError
+RA.escapeRegExp(undefined); // $ExpectError
+RA.escapeRegExp([]); // $ExpectError
diff --git a/types/test/helpers/index.ts b/types/test/helpers/index.ts
new file mode 100644
index 0000000000..bf3e77ae64
--- /dev/null
+++ b/types/test/helpers/index.ts
@@ -0,0 +1,7 @@
+export const customIterable: Iterable<number> = {
+    *[Symbol.iterator](): IterableIterator<number> {
+        yield 1;
+        yield 2;
+        yield 3;
+    },
+};
diff --git a/types/test/isSparseArray.ts b/types/test/isSparseArray.ts
index d713dbcdf9..06864906c6 100644
--- a/types/test/isSparseArray.ts
+++ b/types/test/isSparseArray.ts
@@ -1,11 +1,8 @@
 import * as RA from 'ramda-adjunct';
 
-/**
- * Tests.
- */
-
 RA.isSparseArray(new Array(3)); // $ExpectType boolean
-RA.isSparseArray([1, , 3]); // tslint:disable-line no-sparse-arrays
+// tslint:disable-next-line:no-sparse-arrays
+RA.isSparseArray([1, , 3]); // $ExpectType boolean
 
 RA.isSparseArray([1, 2, 3]); // $ExpectType boolean
 RA.isSparseArray(new Array()); // $ExpectType boolean
diff --git a/types/test/pathOrLazy.ts b/types/test/pathOrLazy.ts
new file mode 100644
index 0000000000..bf6422d112
--- /dev/null
+++ b/types/test/pathOrLazy.ts
@@ -0,0 +1,6 @@
+import * as RA from 'ramda-adjunct';
+
+RA.pathOrLazy(() => 7, ['a', 1, 'b'], {}); // $ExpectType number
+RA.pathOrLazy(() => 7, ['a', 1, 'b'])({}); // $ExpectType number
+RA.pathOrLazy(() => 7)(['a', 1, 'b'], {}); // $ExpectType number
+RA.pathOrLazy(() => 7)(['a', 1, 'b'])({}); // $ExpectType number
diff --git a/types/test/subtractValue.ts b/types/test/subtractValue.ts
index 125bc81e98..0669919869 100644
--- a/types/test/subtractValue.ts
+++ b/types/test/subtractValue.ts
@@ -2,8 +2,15 @@ import * as RA from 'ramda-adjunct';
 
 RA.subtractNum(3, 5); // $ExpectType number
 RA.subtractNum(5, 3); // $ExpectType number
-RA.subtractNum(3, 'foo'); // $ExpectError
 
 RA.subtractNum(3)(5); // $ExpectType number
 RA.subtractNum(5)(3); // $ExpectType number
-RA.subtractNum(3)('foo'); // $ExpectError
+
+RA.subtractNum('a', 'b'); // $ExpectError
+RA.subtractNum('a')('b'); // $ExpectError
+RA.subtractNum(1, undefined); // $ExpectError
+RA.subtractNum(1)(undefined); // $ExpectError
+RA.subtractNum(1, {}); // $ExpectError
+RA.subtractNum(1)({}); // $ExpectError
+RA.subtractNum({}, 1); // $ExpectError
+RA.subtractNum({})(1); // $ExpectError