Repository: marpple/FxTS (ts)

Issue:
Add `average` function
# Suggestion

<!--
  Please fill in each section completely. Thank you!
-->

## ⭐ Suggestion

<!-- What would you like to be added? -->

## 💻 Use Cases

```ts
average([1,2,3,4,5]); // 3
await average(toAsync([1,2,3,4,5])); // 3
```


Pull request description:
feat: add `average` function
Fixes #77 


Patch:
diff --git a/src/Lazy/compress.ts b/src/Lazy/compress.ts
new file mode 100644
index 00000000..71d0a9fd
--- /dev/null
+++ b/src/Lazy/compress.ts
@@ -0,0 +1,69 @@
+import zip from "./zip";
+import pipe from "../pipe";
+import filter from "./filter";
+import map from "./map";
+import ReturnIterableIteratorType from "../types/ReturnIterableIteratorType";
+
+/**
+ * Returns Iterable/AsyncIterable that filters elements from 'iterable' returning only those that have a corresponding element in 'selectors' that evaluates to 'true'.
+ * Stops when either 'iterable' or 'selectors' has been exhausted.
+ *
+ * @example
+ * ```ts
+ * const iter1 = compress([false,true,false,false,true],  [1, 2, 3, 4, 5]);
+ * iter1.next(); // {value: 2, done:false}
+ * iter1.next(); // {value: 5, done:false}
+ * iter1.next(); // {value: undefined, done:true }
+ *
+ * const iter2 = compress([1,0,0,1,0],  "abcde");
+ * iter2.next(); // {value: "a", done:false}
+ * iter2.next(); // {value: "d", done:false}
+ * iter2.next(); // {value: undefined, done:true }
+ *
+ * // with pipe
+ * pipe(
+ *   [1, 2, 3],
+ *   compress([false, true, true]),
+ *   toArray,
+ * ); // [2, 3]
+ * ```
+ *
+ * see {@link https://fxts.dev/docs/pipe | pipe}, {@link https://fxts.dev/docs/toArray | toArray}
+ */
+function compress<A, B>(
+  selector: Array<A>,
+  iterable: Iterable<B>,
+): IterableIterator<B>;
+
+function compress<A, B>(
+  selector: Array<A>,
+  iterable: AsyncIterable<B>,
+): AsyncIterableIterator<B>;
+
+function compress<A, B extends Iterable<unknown> | AsyncIterable<unknown>>(
+  selector: Array<A>,
+): (iterable: B) => ReturnIterableIteratorType<B>;
+
+function compress<A, B extends Iterable<unknown> | AsyncIterable<unknown>>(
+  selectors: Array<A>,
+  iterable?: B,
+):
+  | ReturnIterableIteratorType<B>
+  | ((iterable: B) => ReturnIterableIteratorType<B>) {
+  if (iterable === undefined) {
+    return (iterable: B) => {
+      return compress(
+        selectors,
+        iterable as any,
+      ) as ReturnIterableIteratorType<B>;
+    };
+  }
+
+  return pipe(
+    zip(selectors, iterable),
+    filter(([selector]) => selector),
+    map(([, value]) => value),
+  ) as ReturnIterableIteratorType<B>;
+}
+
+export default compress;
diff --git a/src/Lazy/index.ts b/src/Lazy/index.ts
index 03364915..fb5fa678 100644
--- a/src/Lazy/index.ts
+++ b/src/Lazy/index.ts
@@ -1,6 +1,7 @@
 import append from "./append";
 import chunk from "./chunk";
 import compact from "./compact";
+import compress from "./compress";
 import concat from "./concat";
 import concurrent from "./concurrent";
 import cycle from "./cycle";
@@ -22,6 +23,8 @@ import range from "./range";
 import reject from "./reject";
 import repeat from "./repeat";
 import reverse from "./reverse";
+import scan from "./scan";
+import slice from "./slice";
 import split from "./split";
 import take from "./take";
 import takeUntil from "./takeUntil";
@@ -37,6 +40,7 @@ export {
   append,
   chunk,
   compact,
+  compress,
   concat,
   concurrent,
   cycle,
@@ -58,6 +62,8 @@ export {
   reject,
   repeat,
   reverse,
+  scan,
+  slice,
   split,
   take,
   takeUntil,
diff --git a/src/Lazy/scan.ts b/src/Lazy/scan.ts
new file mode 100644
index 00000000..387f139b
--- /dev/null
+++ b/src/Lazy/scan.ts
@@ -0,0 +1,202 @@
+import pipe1 from "../pipe1";
+import Arrow from "../types/Arrow";
+import Awaited from "../types/Awaited";
+import IterableInfer from "../types/IterableInfer";
+import ReturnIterableIteratorType from "../types/ReturnIterableIteratorType";
+import { empty, isAsyncIterable, isIterable } from "../_internal/utils";
+import concurrent, { isConcurrent } from "./concurrent";
+import head from "../head";
+
+function* sync<A, B>(
+  f: (a: B, b: A) => B,
+  acc: B,
+  iterable: Iterable<A>,
+): IterableIterator<B> {
+  yield acc;
+  for (const a of iterable) {
+    yield (acc = f(acc, a));
+  }
+}
+
+async function* asyncSequential<A, B>(
+  f: (a: B, b: A) => B,
+  acc: Promise<B>,
+  iterable: AsyncIterable<A>,
+): AsyncIterableIterator<B> {
+  yield acc;
+  for await (const a of iterable) {
+    yield (acc = pipe1(acc, (acc) => f(acc as B, a)));
+  }
+}
+
+function async<A, B>(
+  f: (a: B, b: A) => B,
+  acc: Promise<B>,
+  iterable: AsyncIterable<A>,
+): AsyncIterableIterator<B> {
+  let _iterator: AsyncIterator<B>;
+  return {
+    async next(_concurrent: any) {
+      if (_iterator === undefined) {
+        _iterator = isConcurrent(_concurrent)
+          ? asyncSequential(f, acc, concurrent(_concurrent.length, iterable))
+          : asyncSequential(f, acc, iterable);
+      }
+      return _iterator.next(_concurrent);
+    },
+    [Symbol.asyncIterator]() {
+      return this;
+    },
+  };
+}
+
+function asyncWithoutSeed<A, B>(
+  f: (a: B, b: A) => B,
+  iterable: AsyncIterable<A>,
+): AsyncIterableIterator<B> {
+  let _iterator: AsyncIterator<B>;
+  return {
+    async next(_concurrent: any) {
+      if (_iterator === undefined) {
+        if (isConcurrent(_concurrent)) {
+          const _iterable = concurrent(_concurrent.length, iterable);
+          _iterator = asyncSequential(f, head(_iterable) as any, _iterable);
+        } else {
+          _iterator = asyncSequential(f, head(iterable) as any, iterable);
+        }
+      }
+      return _iterator.next(_concurrent);
+    },
+    [Symbol.asyncIterator]() {
+      return this;
+    },
+  };
+}
+
+/**
+ * Returns a Iterable/AsyncIterable of successively reduced values from the left.
+ * It's similar to reduce
+ *
+ * @example
+ * ```ts
+ * const iter = scan((acc, cur) => acc * cur, 1, [1, 2, 3, 4 ]);
+ * iter.next(); // {value: 1, done:false}
+ * iter.next(); // {value: 1, done:false}
+ * iter.next(); // {value: 2, done:false}
+ * iter.next(); // {value: 6, done:false}
+ * iter.next(); // {value: 24, done:false}
+ * iter.next(); // {value: undefined, done: true}
+ *
+ * // with pipe
+ * pipe(
+ *   [1, 2, 3, 4],
+ *   scan,
+ *   toArray,
+ * ); // [1, 2, 6, 24]
+ * ```
+ */
+function scan<A extends readonly []>(
+  f: Arrow,
+  iterable: A,
+): IterableIterator<never>;
+
+function scan<A extends readonly [], B>(
+  f: Arrow,
+  seed: B,
+  iterable: A,
+): IterableIterator<B>;
+
+function scan<A>(
+  f: (a: A, b: A) => A,
+  iterable: Iterable<A>,
+): IterableIterator<A>;
+
+function scan<A, B>(
+  f: (a: B, b: A) => B,
+  seed: B,
+  iterable: Iterable<A>,
+): IterableIterator<B>;
+
+function scan<A, B>(
+  f: (a: B, b: A) => B,
+  iterable: Iterable<A>,
+): IterableIterator<B>;
+
+function scan<A>(
+  f: (a: A, b: A) => A | Promise<A>,
+  iterable: AsyncIterable<A>,
+): AsyncIterableIterator<A>;
+
+function scan<A, B>(
+  f: (a: Awaited<B>, b: A) => B | Promise<B>,
+  seed: B | Promise<B>,
+  iterable: AsyncIterable<A>,
+): AsyncIterableIterator<B>;
+
+function scan<A, B>(
+  f: (a: Awaited<B>, b: A) => B | Promise<B>,
+  iterable: AsyncIterable<A>,
+): AsyncIterableIterator<Awaited<B>>;
+
+function scan<A extends Iterable<unknown> | AsyncIterable<unknown>>(
+  f: (
+    a: IterableInfer<A>,
+    b: IterableInfer<A>,
+  ) => IterableInfer<A> | Promise<IterableInfer<A>>,
+): (iterable: A) => ReturnIterableIteratorType<A, IterableInfer<A>>;
+
+function scan<A extends Iterable<unknown> | AsyncIterable<unknown>, B>(
+  f: (a: B, b: IterableInfer<A>) => B | Promise<B>,
+): (iterable: A) => ReturnIterableIteratorType<A, B>;
+
+function scan<A extends Iterable<unknown> | AsyncIterable<unknown>, B>(
+  f: (a: B, b: IterableInfer<A>) => B,
+  seed?: B | Iterable<IterableInfer<A>> | AsyncIterable<IterableInfer<A>>,
+  iterable?: Iterable<IterableInfer<A>> | AsyncIterable<IterableInfer<A>>,
+):
+  | IterableIterator<B>
+  | AsyncIterableIterator<B>
+  | ((iterable: A) => ReturnIterableIteratorType<A, B>) {
+  if (iterable === undefined) {
+    if (seed === undefined) {
+      return (iterable: A) => {
+        return scan(
+          f,
+          iterable as any,
+        ) as unknown as ReturnIterableIteratorType<A, B>;
+      };
+    }
+
+    if (isIterable(seed)) {
+      const iterator = seed[Symbol.iterator]();
+      const { done, value } = iterator.next();
+      if (done) {
+        return empty() as IterableIterator<B>;
+      }
+
+      return sync(f, value, {
+        [Symbol.iterator]() {
+          return iterator;
+        },
+      });
+    }
+
+    if (isAsyncIterable(seed)) {
+      return asyncWithoutSeed(f, seed);
+    }
+
+    throw new TypeError("'iterable' must be type of Iterable or AsyncIterable");
+  }
+
+  if (isIterable(iterable)) {
+    return sync(f, seed as B, iterable);
+  }
+
+  if (isAsyncIterable(iterable)) {
+    return async(f, Promise.resolve(seed as B), iterable);
+  }
+
+  throw new TypeError("'iterable' must be type of Iterable or AsyncIterable");
+}
+
+export default scan;
diff --git a/src/Lazy/slice.ts b/src/Lazy/slice.ts
new file mode 100644
index 00000000..80446245
--- /dev/null
+++ b/src/Lazy/slice.ts
@@ -0,0 +1,176 @@
+import isNumber from "../isNumber";
+import ReturnIterableIteratorType from "../types/ReturnIterableIteratorType";
+import { isAsyncIterable, isIterable } from "../_internal/utils";
+import concurrent, { isConcurrent } from "./concurrent";
+
+function* sync<T>(
+  start: number,
+  end: number,
+  iterable: Iterable<T>,
+): IterableIterator<T> {
+  let i = 0;
+  for (const item of iterable) {
+    if (i >= start && i < end) {
+      yield item;
+    }
+    i += 1;
+  }
+}
+
+async function* asyncSequential<T>(
+  start: number,
+  end: number,
+  iterable: AsyncIterable<T>,
+): AsyncIterableIterator<T> {
+  let i = 0;
+  for await (const item of iterable) {
+    if (i >= start && i < end) {
+      yield item;
+    }
+    i += 1;
+  }
+}
+
+function async<T>(
+  start: number,
+  end: number,
+  iterable: AsyncIterable<T>,
+): AsyncIterableIterator<T> {
+  let iterator: AsyncIterator<T>;
+  return {
+    [Symbol.asyncIterator]() {
+      return this;
+    },
+
+    async next(_concurrent: any) {
+      if (iterator === undefined) {
+        // prettier-ignore
+        iterator = isConcurrent(_concurrent)
+          ? asyncSequential(start, end, concurrent(_concurrent.length, iterable))
+          : asyncSequential(start, end, iterable);
+      }
+
+      return iterator.next(_concurrent);
+    },
+  };
+}
+
+function _slice<T extends Iterable<unknown> | AsyncIterable<unknown>>(
+  start: number,
+  end: number,
+  iterable: T,
+) {
+  if (!isNumber(start) || !isNumber(end)) {
+    throw new TypeError("'start' and 'end' must be type of number");
+  }
+
+  if (isIterable(iterable)) {
+    return sync(start, end, iterable);
+  }
+
+  if (isAsyncIterable(iterable)) {
+    return async(start, end, iterable);
+  }
+
+  throw new TypeError("'iterable' must be type of Iterable or AsyncIterable");
+}
+
+/**
+ * Returns Iterable/AsyncIterable of the given elements from startIndex(inclusive) to endIndex(exclusive).
+ *
+ * @example
+ * ```ts
+ * const iter1 = slice(1, 3, ['a', 'b', 'c', 'd', 'e']);
+ * iter1.next(); // {value: 'b', done: false};
+ * iter1.next(); // {value: 'c', done: false};
+ * iter1.next(); // {value: undefined, done: true};
+ *
+ * const iter2 = slice(3, ['a', 'b', 'c', 'd', 'e']);
+ * iter2.next(); // {value: 'd', done: false};
+ * iter2.next(); // {value: 'e', done: false};
+ * iter2.next(); // {value: undefined, done: true};
+ *
+ * const iter3 = slice(3, "abcde");
+ * iter3.next(); // {value: 'd', done: false};
+ * iter3.next(); // {value: 'e', done: false};
+ * iter3.next(); // {value: undefined, done: true};
+ *
+ * // with pipe
+ * pipe(
+ *  [1, 2, 3, 4, 5],
+ *  slice(3),
+ *  toArray,
+ * ); // [4, 5]
+ *
+ * pipe(
+ *  [1, 2, 3, 4, 5],
+ *  slice(1, 3),
+ *  toArray,
+ * ); // [2, 3]
+ *
+ * see {@link https://fxts.dev/docs/pipe | pipe}, {@link https://fxts.dev/docs/toArray | toArray}
+ * ```
+ */
+// prettier-ignore
+function slice<T>(
+  start: number, 
+  iterable: Iterable<T>
+): IterableIterator<T>;
+
+function slice<T>(
+  start: number,
+  iterable: AsyncIterable<T>,
+): AsyncIterableIterator<T>;
+
+function slice<T>(
+  start: number,
+  end: number,
+  iterable: Iterable<T>,
+): IterableIterator<T>;
+
+function slice<T>(
+  start: number,
+  end: number,
+  iterable: AsyncIterable<T>,
+): AsyncIterableIterator<T>;
+
+function slice<A extends Iterable<unknown> | AsyncIterable<unknown>>(
+  start: number,
+): (iterable: A) => ReturnIterableIteratorType<A>;
+
+function slice<A extends Iterable<unknown> | AsyncIterable<unknown>>(
+  start: number,
+  end: number,
+): (iterable: A) => ReturnIterableIteratorType<A>;
+
+function slice<T extends Iterable<unknown> | AsyncIterable<unknown>>(
+  start: number | T,
+  end?: number | T,
+  iterable?: T,
+) {
+  if (iterable === undefined) {
+    if (end === undefined) {
+      return (iterable: T) => {
+        return _slice(start as number, Infinity, iterable);
+      };
+    }
+
+    if (isIterable(end) || isAsyncIterable(end)) {
+      return _slice(start as number, Infinity, end);
+    }
+
+    if (isNumber(end)) {
+      return (iterable: T) => {
+        return _slice(start as number, end, iterable);
+      };
+    }
+
+    return (iterable: T) => {
+      return _slice(0, Infinity, iterable);
+    };
+  }
+
+  return _slice(start as number, end as number, iterable as T);
+}
+
+export default slice;
diff --git a/src/average.ts b/src/average.ts
new file mode 100644
index 00000000..bc6cf0e9
--- /dev/null
+++ b/src/average.ts
@@ -0,0 +1,50 @@
+import peek from "./Lazy/peek";
+import pipe from "./pipe";
+import sum from "./sum";
+import ReturnValueType from "./types/ReturnValueType";
+import { isAsyncIterable, isIterable } from "./_internal/utils";
+
+/**
+ * Returns the average of the given (Iterable/AsyncIterable)<number> (mean)
+ *
+ * @example
+ * ```ts
+ * average([1, 2, 3, 4, 5]); // 6
+ * await average(toAsync([1, 2, 3, 4, 5])); // 6
+ *
+ * // with pipe
+ * pipe(
+ *  [1, 2, 3, 4, 5],
+ *  average,
+ * ); // 6
+ * ```
+ *
+ * see {@link https://fxts.dev/docs/pipe | pipe}
+ */
+function average<T extends Iterable<number> | AsyncIterable<number>>(
+  iterable: T,
+): ReturnValueType<T> {
+  let size = 0;
+
+  if (isIterable(iterable)) {
+    return pipe(
+      iterable as Iterable<number>,
+      peek(() => size++),
+      sum,
+      (a) => (size === 0 ? NaN : a / size),
+    ) as ReturnValueType<T>;
+  }
+
+  if (isAsyncIterable(iterable)) {
+    return pipe(
+      iterable as AsyncIterable<number>,
+      peek(() => size++),
+      sum,
+      (a) => (size === 0 ? NaN : a / size),
+    ) as ReturnValueType<T>;
+  }
+
+  throw new TypeError("'iterable' must be type of Iterable or AsyncIterable");
+}
+
+export default average;
diff --git a/src/index.ts b/src/index.ts
index d33e4756..aa4c9952 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,5 +1,6 @@
 import add from "./add";
 import apply from "./apply";
+import average from "./average";
 import consume from "./consume";
 import countBy from "./countBy";
 import curry from "./curry";
@@ -39,6 +40,7 @@ import reduce from "./reduce";
 import size from "./size";
 import some from "./some";
 import sort from "./sort";
+import sortBy from "./sortBy";
 import sum from "./sum";
 import tap from "./tap";
 import toArray from "./toArray";
@@ -46,6 +48,8 @@ import toArray from "./toArray";
 export {
   add,
   apply,
+  average,
+  average as mean,
   consume,
   countBy,
   curry,
@@ -87,6 +91,7 @@ export {
   size,
   some,
   sort,
+  sortBy,
   sum,
   tap,
   toArray,
diff --git a/src/sort.ts b/src/sort.ts
index a4fd5f4a..27e3857f 100644
--- a/src/sort.ts
+++ b/src/sort.ts
@@ -6,7 +6,7 @@ import pipe1 from "./pipe1";
 import toArray from "./toArray";
 
 /**
- * Returns an array which is sorted by function `f`.
+ * Returns an array, sorted according to the comparator `f`, which should accept two values
  *
  * @example
  * ```ts
@@ -36,7 +36,7 @@ function sort<T extends Iterable<unknown> | AsyncIterable<unknown>>(
 ): (iterable: T) => ReturnValueType<T, IterableInfer<T>[]>;
 
 function sort<T extends Iterable<unknown> | AsyncIterable<unknown>>(
-  f: (a: T, b: T) => unknown,
+  f: (a: IterableInfer<T>, b: IterableInfer<T>) => unknown,
   iterable?: T,
 ):
   | IterableInfer<T>[]
diff --git a/src/sortBy.ts b/src/sortBy.ts
new file mode 100644
index 00000000..11518c9a
--- /dev/null
+++ b/src/sortBy.ts
@@ -0,0 +1,69 @@
+import isArray from "./isArray";
+import pipe1 from "./pipe1";
+import toArray from "./toArray";
+import IterableInfer from "./types/IterableInfer";
+import ReturnValueType from "./types/ReturnValueType";
+import { isAsyncIterable, isIterable } from "./_internal/utils";
+
+/**
+ * Returns an array which sorted according to the `f`.
+ *
+ * @example
+ * ```ts
+ * sortBy(a => a, [3, 4, 1, 2, 5, 2]);
+ * // [1, 2, 2, 3, 4, 5]
+ * sortBy((a) => a.id, [{id:4,name:'foo'}, {id:2,name:'bar'},{id:3, name:'lee'}]);
+ * // [{id:2,name:'bar'}, {id:3, name:'lee'}, {id:4,name:'foo'}]
+ * ```
+ */
+function sortBy(f: (a: any) => unknown, iterable: readonly []): any[];
+
+function sortBy<T>(f: (a: T) => unknown, iterable: Iterable<T>): T[];
+
+function sortBy<T>(
+  f: (a: T) => unknown,
+  iterable: AsyncIterable<T>,
+): Promise<T[]>;
+
+function sortBy<T extends Iterable<unknown> | AsyncIterable<unknown>>(
+  f: (a: IterableInfer<T>) => unknown,
+): (iterable: T) => ReturnValueType<T, IterableInfer<T>[]>;
+
+function sortBy<T extends Iterable<unknown> | AsyncIterable<unknown>>(
+  f: (a: IterableInfer<T>) => unknown,
+  iterable?: T,
+):
+  | IterableInfer<T>[]
+  | Promise<IterableInfer<T>[]>
+  | ((iterable: T) => ReturnValueType<T, IterableInfer<T>[]>) {
+  if (iterable === undefined) {
+    return (iterable: T) =>
+      sortBy(f, iterable as any) as ReturnValueType<T, IterableInfer<T>[]>;
+  }
+
+  const _sortBy = (a: IterableInfer<T>, b: IterableInfer<T>) => {
+    const aa = f(a) as number;
+    const bb = f(b) as number;
+    return aa < bb ? -1 : aa > bb ? 1 : 0;
+  };
+
+  if (isArray(iterable)) {
+    return iterable.sort(_sortBy);
+  }
+
+  if (isIterable(iterable)) {
+    return pipe1(toArray(iterable as Iterable<IterableInfer<T>>), (arr) =>
+      arr.sort(_sortBy),
+    );
+  }
+
+  if (isAsyncIterable(iterable)) {
+    return pipe1(toArray(iterable as AsyncIterable<IterableInfer<T>>), (arr) =>
+      arr.sort(_sortBy),
+    );
+  }
+
+  throw new TypeError("'iterable' must be type of Iterable or AsyncIterable");
+}
+
+export default sortBy;


Test patch:
diff --git a/test/Lazy/compress.spec.ts b/test/Lazy/compress.spec.ts
new file mode 100644
index 00000000..03213d85
--- /dev/null
+++ b/test/Lazy/compress.spec.ts
@@ -0,0 +1,87 @@
+import { compress, map, pipe, toArray, toAsync } from "../../src";
+import { Concurrent } from "../../src/Lazy/concurrent";
+import { generatorMock } from "../utils";
+
+describe("compress", function () {
+  describe("sync", function () {
+    it.each([
+      [
+        [false, true, false, false, true],
+        [1, 2, 3, 4, 5],
+        [2, 5],
+      ],
+      [
+        [false, true, false, false, true],
+        [1, 2, 3, 4, 5, 6],
+        [2, 5],
+      ],
+      [[false, true, false, false, true], [1, 2, 3, 4], [2]],
+      [[1, 0, 0, 1, 0], "abcde", ["a", "d"]],
+    ])(
+      "should filter elements that have a corresponding element in 'selectors'",
+      function (selectors, iterable, result) {
+        const res = compress(
+          selectors as Array<any>,
+          iterable as Iterable<any>,
+        );
+        expect(toArray(res)).toEqual(result);
+      },
+    );
+
+    it("should be able to be used as a curried function in the pipeline", function () {
+      const res = pipe(
+        [1, 2, 3, 4, 5],
+        compress([false, true, false, false, true]),
+        toArray,
+      );
+
+      expect(res).toEqual([2, 5]);
+    });
+  });
+
+  describe("async", function () {
+    it.each([
+      [
+        [false, true, false, false, true],
+        [1, 2, 3, 4, 5],
+        [2, 5],
+      ],
+      [
+        [false, true, false, false, true],
+        [1, 2, 3, 4, 5, 6],
+        [2, 5],
+      ],
+      [[false, true, false, false, true], [1, 2, 3, 4], [2]],
+      [[1, 0, 0, 1, 0], "abcde", ["a", "d"]],
+    ])(
+      "should filter elements that have a corresponding element in 'selectors'",
+      async function (selectors, iterable, result) {
+        const res = compress(
+          selectors as Array<any>,
+          iterable as Iterable<any>,
+        );
+        expect(await toArray(toAsync(res))).toEqual(result);
+      },
+    );
+
+    it("should be able to be used as a curried function in the pipeline", async function () {
+      const res = await pipe(
+        [1, 2, 3, 4, 5],
+        toAsync,
+        compress([false, true, false, false, true]),
+        toArray,
+      );
+
+      expect(res).toEqual([2, 5]);
+    });
+
+    it("should be passed concurrent object when job works concurrently", async function () {
+      const mock = generatorMock();
+      const iter = map((a) => a, mock);
+      const concurrent = Concurrent.of(2) as any;
+
+      await iter.next(concurrent);
+      expect((mock as any).getConcurrent()).toEqual(concurrent);
+    });
+  });
+});
diff --git a/test/Lazy/scan.spec.ts b/test/Lazy/scan.spec.ts
new file mode 100644
index 00000000..2e78c974
--- /dev/null
+++ b/test/Lazy/scan.spec.ts
@@ -0,0 +1,149 @@
+import { delay, map, pipe, range, toArray, toAsync } from "../../src";
+import concurrent, { Concurrent } from "../../src/Lazy/concurrent";
+import scan from "../../src/Lazy/scan";
+import { callFuncAfterTime, generatorMock } from "../utils";
+
+describe("scan", function () {
+  describe("sync", function () {
+    it.each([
+      [(a: number, b: number) => a * b, 1, [1, 2, 3, 4], [1, 1, 2, 6, 24]],
+      [(a: string, b: string) => a + b, "a", ["b", "c"], ["a", "ab", "abc"]],
+    ])(
+      "should return value that is reduced given elements successively %s %s %s",
+      function (f, seed, iterable, result) {
+        expect(toArray(scan(f, seed, iterable as any))).toEqual(result);
+      },
+    );
+
+    it.each([
+      [(a: number, b: number) => a * b, [1, 2, 3, 4], [1, 2, 6, 24]],
+      [(a: string, b: string) => a + b, ["a", "b", "c"], ["a", "ab", "abc"]],
+    ])(
+      "should reduce given elements successively without seed %s %s %s",
+      function (f, iterable, result) {
+        expect(toArray(scan(f, iterable as any))).toEqual(result);
+      },
+    );
+
+    it("should be able to be used as a curried function in the pipeline", function () {
+      const res = pipe(
+        [1, 2, 3, 4],
+        scan((a, b) => a * b),
+        toArray,
+      );
+
+      expect(res).toEqual([1, 2, 6, 24]);
+    });
+  });
+
+  describe("async", function () {
+    it.each([
+      [(a: number, b: number) => a * b, 1, [1, 2, 3, 4], [1, 1, 2, 6, 24]],
+      [(a: string, b: string) => a + b, "a", ["b", "c"], ["a", "ab", "abc"]],
+    ])(
+      "should return value that is reduced given elements successively %s %s %s",
+      async function (f, seed, iterable, result) {
+        expect(
+          await toArray(scan(f, seed, toAsync(iterable as any) as any)),
+        ).toEqual(result);
+      },
+    );
+
+    it.each([
+      [(a: number, b: number) => a * b, 1, [1, 2, 3, 4], [1, 1, 2, 6, 24]],
+      [(a: string, b: string) => a + b, "a", ["b", "c"], ["a", "ab", "abc"]],
+    ])(
+      "should reduce given elements successively when seed is Promise %s %s %s",
+      async function (f, seed, iterable, result) {
+        expect(
+          await toArray(
+            scan(f, Promise.resolve(seed), toAsync(iterable as any) as any),
+          ),
+        ).toEqual(result);
+      },
+    );
+
+    it.each([
+      [(a: number, b: number) => a * b, [1, 2, 3, 4], [1, 2, 6, 24]],
+      [(a: string, b: string) => a + b, ["a", "b", "c"], ["a", "ab", "abc"]],
+    ])(
+      "should reduce given elements successively without seed %s %s %s",
+      async function (f, iterable, result) {
+        expect(await toArray(scan(f, toAsync(iterable as any) as any))).toEqual(
+          result,
+        );
+      },
+    );
+
+    it("should be able to be used as a curried function in the pipeline", async function () {
+      const res = await pipe(
+        [1, 2, 3, 4],
+        toAsync,
+        scan((a, b) => a * b),
+        toArray,
+      );
+
+      expect(res).toEqual([1, 2, 6, 24]);
+    });
+
+    it("should be passed concurrent object when job works concurrently", async function () {
+      const mock = generatorMock();
+      const iter = scan((a) => a, mock);
+      const concurrent = Concurrent.of(2) as any;
+
+      await iter.next(concurrent);
+      expect((mock as any).getConcurrent()).toEqual(concurrent);
+    });
+
+    it("should be handled concurrently", async function () {
+      const fn = jest.fn();
+      callFuncAfterTime(fn, 600);
+
+      const res = await pipe(
+        [1, 2, 3, 4, 5, 6, 7, 8, 9],
+        toAsync,
+        map((a) => delay(200, a)),
+        (nums) => scan((a, b) => a * b, 1, nums),
+        concurrent(3),
+        toArray,
+      );
+
+      expect(fn).toBeCalled();
+      expect(res).toEqual([1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880]);
+    }, 650);
+
+    it("should be handled concurrently without seed", async function () {
+      const fn = jest.fn();
+      callFuncAfterTime(fn, 600);
+
+      const res = await pipe(
+        [1, 2, 3, 4, 5, 6, 7, 8, 9],
+        toAsync,
+        map((a) => delay(200, a)),
+        scan((a, b) => a * b),
+        concurrent(3),
+        toArray,
+      );
+
+      expect(fn).toBeCalled();
+      expect(res).toEqual([1, 2, 6, 24, 120, 720, 5040, 40320, 362880]);
+    }, 650);
+
+    it("should be able to handle an error when working concurrent", async function () {
+      await expect(
+        pipe(
+          toAsync(range(1, 21)),
+          map((a) => delay(100, a)),
+          scan((a, b) => {
+            if (a * b === 24) {
+              throw new Error("err");
+            }
+            return a * b;
+          }),
+          concurrent(2),
+          toArray,
+        ),
+      ).rejects.toThrow("err");
+    });
+  });
+});
diff --git a/test/Lazy/slice.spec.ts b/test/Lazy/slice.spec.ts
new file mode 100644
index 00000000..a6023bae
--- /dev/null
+++ b/test/Lazy/slice.spec.ts
@@ -0,0 +1,95 @@
+import { pipe, slice, toArray, toAsync } from "../../src";
+import { Concurrent } from "../../src/Lazy/concurrent";
+import { generatorMock } from "../utils";
+
+describe("slice", function () {
+  describe("sync", function () {
+    it.each([
+      [1, 3, [1, 2, 3, 4, 5], [2, 3]],
+      [-1, 3, [1, 2, 3, 4, 5], [1, 2, 3]],
+      [2, Infinity, [1, 2, 3, 4, 5], [3, 4, 5]],
+      [7, 3, [1, 2, 3, 4, 5], []],
+      [1, 3, "abcde", ["b", "c"]],
+    ])(
+      "should return elements from startIndex(%i) to endIndex(%i) of %s",
+      function (startIndex, endIndex, iterable, result) {
+        expect(
+          toArray(slice(startIndex, endIndex, iterable as Iterable<any>)),
+        ).toEqual(result);
+      },
+    );
+
+    it.each([
+      [1, [1, 2, 3, 4, 5], [2, 3, 4, 5]],
+      [-1, [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]],
+      [2, [1, 2, 3, 4, 5], [3, 4, 5]],
+      [7, [1, 2, 3, 4, 5], []],
+      [1, "abcde", ["b", "c", "d", "e"]],
+    ])(
+      "should return elements from startIndex(%i) to end of %s",
+      function (startIndex, iterable, result) {
+        expect(toArray(slice(startIndex, iterable as Iterable<any>))).toEqual(
+          result,
+        );
+      },
+    );
+
+    it("should be able to be used as a curried function in the pipeline", function () {
+      const res1 = pipe([1, 2, 3, 4, 5], slice(2), toArray);
+      expect(res1).toEqual([3, 4, 5]);
+
+      const res2 = pipe([1, 2, 3, 4, 5], slice(1, 3), toArray);
+      expect(res2).toEqual([2, 3]);
+    });
+  });
+
+  describe("async", function () {
+    it.each([
+      [1, 3, [1, 2, 3, 4, 5], [2, 3]],
+      [-1, 3, [1, 2, 3, 4, 5], [1, 2, 3]],
+      [2, Infinity, [1, 2, 3, 4, 5], [3, 4, 5]],
+      [7, 3, [1, 2, 3, 4, 5], []],
+      [1, 3, "abcde", ["b", "c"]],
+    ])(
+      "should return elements from startIndex(%i) to endIndex(%i) of %s",
+      async function (startIndex, endIndex, iterable, result) {
+        expect(
+          await toArray(
+            slice(startIndex, endIndex, toAsync(iterable as Iterable<any>)),
+          ),
+        ).toEqual(result);
+      },
+    );
+
+    it.each([
+      [1, [1, 2, 3, 4, 5], [2, 3, 4, 5]],
+      [-1, [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]],
+      [2, [1, 2, 3, 4, 5], [3, 4, 5]],
+      [7, [1, 2, 3, 4, 5], []],
+      [1, "abcde", ["b", "c", "d", "e"]],
+    ])(
+      "should return elements from startIndex(%i) to end of %s",
+      async function (startIndex, iterable, result) {
+        expect(
+          await toArray(slice(startIndex, toAsync(iterable as Iterable<any>))),
+        ).toEqual(result);
+      },
+    );
+
+    it("should be able to be used as a curried function in the pipeline", async function () {
+      const res1 = await pipe([1, 2, 3, 4, 5], toAsync, slice(2), toArray);
+      expect(res1).toEqual([3, 4, 5]);
+
+      const res2 = await pipe([1, 2, 3, 4, 5], toAsync, slice(1, 3), toArray);
+      expect(res2).toEqual([2, 3]);
+    });
+
+    it("should be passed concurrent object when job works concurrently", async function () {
+      const mock = generatorMock();
+      const iter = slice(1, 2, mock);
+      const concurrent = Concurrent.of(2) as any;
+      await iter.next(concurrent);
+      expect((mock as any).getConcurrent()).toEqual(concurrent);
+    });
+  });
+});
diff --git a/test/average.spec.ts b/test/average.spec.ts
new file mode 100644
index 00000000..7a928a4a
--- /dev/null
+++ b/test/average.spec.ts
@@ -0,0 +1,37 @@
+import { average, pipe, toAsync } from "../src";
+
+describe("average", function () {
+  describe("sync", function () {
+    it.each([
+      [[1, 2, 3, 4, 5], 3],
+      [[], NaN],
+    ])(
+      "should return the average of the given elements",
+      function (nums, result) {
+        expect(average(nums)).toEqual(result);
+      },
+    );
+
+    it("should be able to be used as a curried function in the pipeline", function () {
+      const res = pipe([1, 2, 3, 4, 5], average);
+      expect(res).toEqual(3);
+    });
+  });
+
+  describe("async", function () {
+    it.each([
+      [[1, 2, 3, 4, 5], 3],
+      [[], NaN],
+    ])(
+      "should return the average of the given elements",
+      async function (nums, result) {
+        expect(await average(toAsync(nums))).toEqual(result);
+      },
+    );
+
+    it("should be able to be used as a curried function in the pipeline", async function () {
+      const res = await pipe([1, 2, 3, 4, 5], toAsync, average);
+      expect(res).toEqual(3);
+    });
+  });
+});
diff --git a/test/sort.spec.ts b/test/sort.spec.ts
index 8bd6f620..e8654422 100644
--- a/test/sort.spec.ts
+++ b/test/sort.spec.ts
@@ -12,10 +12,15 @@ describe("sort", function () {
   };
 
   describe("sync", function () {
-    it("should return 'false' if given iterable is an empty array", function () {
-      expect(sort(sortFn, [])).toEqual([]);
-      expect(sort(sortFn, [3, 4, 1, 2, 5, 2])).toEqual([1, 2, 2, 3, 4, 5]);
-      expect(sort(sortFn, "bcdaef")).toEqual(["a", "b", "c", "d", "e", "f"]);
+    it.each([
+      [[], []],
+      [
+        [3, 4, 1, 2, 5, 2],
+        [1, 2, 2, 3, 4, 5],
+      ],
+      ["bcdaef", ["a", "b", "c", "d", "e", "f"]],
+    ])("should sort the elements", function (iterable, result) {
+      expect(sort(sortFn, iterable as Iterable<any>)).toEqual(result);
     });
 
     it("should be able to be used as a curried function in the pipeline", function () {
@@ -29,29 +34,16 @@ describe("sort", function () {
   });
 
   describe("async", function () {
-    it("should return 'false' if given iterable is an empty array", async function () {
-      const sortFn = (a: number | string, b: number | string) => {
-        if (a === b) {
-          return 0;
-        }
-        if (a > b) {
-          return 1;
-        }
-        return -1;
-      };
-
-      expect(await sort(sortFn, toAsync([]))).toEqual([]);
-      expect(await sort(sortFn, toAsync([3, 4, 1, 2, 5, 2]))).toEqual([
-        1, 2, 2, 3, 4, 5,
-      ]);
-      expect(await sort(sortFn, toAsync("bcdaef"))).toEqual([
-        "a",
-        "b",
-        "c",
-        "d",
-        "e",
-        "f",
-      ]);
+    it.each([
+      [[], []],
+      [
+        [3, 4, 1, 2, 5, 2],
+        [1, 2, 2, 3, 4, 5],
+      ],
+      ["bcdaef", ["a", "b", "c", "d", "e", "f"]],
+    ])("should sort the elements", async function (iterable, result) {
+      const res = await sort(sortFn, toAsync(iterable as Iterable<any>));
+      expect(res).toEqual(result);
     });
 
     it("should be able to be used as a curried function in the pipeline", async function () {
diff --git a/test/sortBy.spec.ts b/test/sortBy.spec.ts
new file mode 100644
index 00000000..c3034781
--- /dev/null
+++ b/test/sortBy.spec.ts
@@ -0,0 +1,69 @@
+import { filter, identity, pipe, sortBy, toAsync } from "../src/index";
+
+describe("sortBy", function () {
+  describe("sync", function () {
+    it.each([
+      [identity, [], []],
+      [identity, [3, 4, 1, 2, 5, 2], [1, 2, 2, 3, 4, 5]],
+      [identity, "bcdae", ["a", "b", "c", "d", "e"]],
+      [
+        (a: any) => a.id,
+        [
+          { id: 4, name: "foo" },
+          { id: 2, name: "bar" },
+          { id: 3, name: "lee" },
+        ],
+        [
+          { id: 2, name: "bar" },
+          { id: 3, name: "lee" },
+          { id: 4, name: "foo" },
+        ],
+      ],
+    ])("should sort to elements by 'f'", function (f, iterable, result) {
+      expect(sortBy(f, iterable as Iterable<any>)).toEqual(result);
+    });
+
+    it("should be able to be used as a curried function in the pipeline", function () {
+      const res = pipe(
+        [3, 4, 1, 2, 5, 2],
+        filter((a) => a % 2 !== 0),
+        sortBy(identity),
+      );
+      expect(res).toEqual([1, 3, 5]);
+    });
+  });
+
+  describe("async", function () {
+    it.each([
+      [identity, [], []],
+      [identity, [3, 4, 1, 2, 5, 2], [1, 2, 2, 3, 4, 5]],
+      [identity, "bcdae", ["a", "b", "c", "d", "e"]],
+      [
+        (a: any) => a.id,
+        [
+          { id: 4, name: "foo" },
+          { id: 2, name: "bar" },
+          { id: 3, name: "lee" },
+        ],
+        [
+          { id: 2, name: "bar" },
+          { id: 3, name: "lee" },
+          { id: 4, name: "foo" },
+        ],
+      ],
+    ])("should sort to elements by 'f'", async function (f, iterable, result) {
+      const res = await sortBy(f, toAsync(iterable as Iterable<any>));
+      expect(res).toEqual(result);
+    });
+
+    it("should be able to be used as a curried function in the pipeline", async function () {
+      const res = await pipe(
+        [3, 4, 1, 2, 5, 2],
+        toAsync,
+        filter((a) => a % 2 !== 0),
+        sortBy(identity),
+      );
+      expect(res).toEqual([1, 3, 5]);
+    });
+  });
+});
diff --git a/type-check/Lazy/compress.test.ts b/type-check/Lazy/compress.test.ts
new file mode 100644
index 00000000..2ef34dbb
--- /dev/null
+++ b/type-check/Lazy/compress.test.ts
@@ -0,0 +1,24 @@
+import * as Test from "../../src/types/Test";
+import { toAsync, compress, pipe } from "../../src";
+
+const { checks, check } = Test;
+
+const res1 = compress([], []);
+const res2 = compress([true, false], [1, 0]);
+const res3 = compress([true, false], "ab");
+const res4 = compress([true, 0], [1, 0]);
+const res5 = compress([true, false], toAsync([1, 0]));
+const res6 = pipe([1, 0], compress([true, false]));
+const res7 = pipe(Promise.resolve([1, 2, 3]), compress([true, false]));
+const res8 = pipe(toAsync([1, 2, 3]), compress([true, false]));
+
+checks([
+  check<typeof res1, IterableIterator<never>, Test.Pass>(),
+  check<typeof res2, IterableIterator<number>, Test.Pass>(),
+  check<typeof res3, IterableIterator<string>, Test.Pass>(),
+  check<typeof res4, IterableIterator<number>, Test.Pass>(),
+  check<typeof res5, AsyncIterableIterator<number>, Test.Pass>(),
+  check<typeof res6, IterableIterator<number>, Test.Pass>(),
+  check<typeof res7, Promise<IterableIterator<number>>, Test.Pass>(),
+  check<typeof res8, AsyncIterableIterator<number>, Test.Pass>(),
+]);
diff --git a/type-check/Lazy/scan.test.ts b/type-check/Lazy/scan.test.ts
new file mode 100644
index 00000000..c45c9642
--- /dev/null
+++ b/type-check/Lazy/scan.test.ts
@@ -0,0 +1,81 @@
+import * as Test from "../../src/types/Test";
+import { pipe, scan, toAsync } from "../../src";
+
+const { checks, check } = Test;
+
+const res1 = scan((a, b) => a + b, []);
+const res2 = scan((a, b) => a + b, "seed", []);
+const res3 = scan((a, b) => a + b, 0, [1, 2, 3]);
+const res4 = scan((a, b) => a + b, [1, 2, 3]);
+const res5 = scan((a, b) => a + b, "0", [1, 2, 3]);
+const res6 = scan((a: string, b) => String(Number(a) + Number(b)), [1, 2, 3]);
+const res7 = scan((a, b) => a + b, toAsync([1, 2, 3]));
+const res8 = scan((a, b) => a + b, 0, toAsync([1, 2, 3]));
+const res9 = scan((a, b) => a + b, Promise.resolve(0), toAsync([1, 2, 3]));
+const res10 = scan(
+  (a: string, b) => String(Number(a) + Number(b)),
+  toAsync([1, 2, 3]),
+);
+const res11 = scan(
+  (a: string, b) => Promise.resolve(String(Number(a) + Number(b))),
+  toAsync([1, 2, 3]),
+);
+
+const res12 = pipe(
+  [1, 2, 3],
+  scan((a, b) => a + b),
+);
+
+const res13 = pipe(
+  [1, 2, 3],
+  scan((a: string, b) => String(Number(a) + Number(b))),
+);
+
+const res14 = pipe(
+  [1, 2, 3],
+  toAsync,
+  scan((a, b) => a + b),
+);
+
+const res15 = pipe(
+  [1, 2, 3],
+  toAsync,
+  scan((a, b) => Promise.resolve(a + b)),
+);
+
+const res16 = pipe(
+  [1, 2, 3],
+  toAsync,
+  scan((a: string, b) => String(Number(a) + Number(b))),
+);
+
+const res17 = pipe(
+  [1, 2, 3],
+  toAsync,
+  scan((a: string, b) => Promise.resolve(String(Number(a) + Number(b)))),
+);
+
+const res18 = pipe([1, 2, 3], toAsync, (res) =>
+  scan((a, b) => Promise.resolve(a + b), 0, res),
+);
+
+checks([
+  check<typeof res1, IterableIterator<never>, Test.Pass>(),
+  check<typeof res2, IterableIterator<string>, Test.Pass>(),
+  check<typeof res3, IterableIterator<number>, Test.Pass>(),
+  check<typeof res4, IterableIterator<number>, Test.Pass>(),
+  check<typeof res5, IterableIterator<string>, Test.Pass>(),
+  check<typeof res6, IterableIterator<string>, Test.Pass>(),
+  check<typeof res7, AsyncIterableIterator<number>, Test.Pass>(),
+  check<typeof res8, AsyncIterableIterator<number>, Test.Pass>(),
+  check<typeof res9, AsyncIterableIterator<number>, Test.Pass>(),
+  check<typeof res10, AsyncIterableIterator<string>, Test.Pass>(),
+  check<typeof res11, AsyncIterableIterator<string>, Test.Pass>(),
+  check<typeof res12, IterableIterator<number>, Test.Pass>(),
+  check<typeof res13, IterableIterator<string>, Test.Pass>(),
+  check<typeof res14, AsyncIterableIterator<number>, Test.Pass>(),
+  check<typeof res15, AsyncIterableIterator<number>, Test.Pass>(),
+  check<typeof res16, AsyncIterableIterator<string>, Test.Pass>(),
+  check<typeof res17, AsyncIterableIterator<string>, Test.Pass>(),
+  check<typeof res18, AsyncIterableIterator<number>, Test.Pass>(),
+]);
diff --git a/type-check/Lazy/slice.test.ts b/type-check/Lazy/slice.test.ts
new file mode 100644
index 00000000..a22858a6
--- /dev/null
+++ b/type-check/Lazy/slice.test.ts
@@ -0,0 +1,47 @@
+import * as Test from "../../src/types/Test";
+import { slice, pipe, toAsync } from "../../src";
+
+const { checks, check } = Test;
+
+const res1 = slice(2, [1, 2, 3, 4]);
+const res2 = slice(1, 2, [1, 2, 3, 4]);
+const res3 = slice(2, "abcde");
+const res4 = slice(1, 2, "abcde");
+
+const res5 = slice(2, toAsync([1, 2, 3, 4]));
+const res6 = slice(1, 2, toAsync([1, 2, 3, 4]));
+
+const res7 = pipe([1, 2, 3, 4, 5, 6, 7], slice(2));
+const res8 = pipe([1, 2, 3, 4, 5, 6, 7], slice(2));
+
+const res9 = pipe([1, 2, 3, 4, 5, 6, 7], slice(2, 3));
+const res10 = pipe([1, 2, 3, 4, 5, 6, 7], slice(2, 3));
+
+const res11 = pipe([1, 2, 3, 4, 5, 6, 7], toAsync, slice(2));
+const res12 = pipe([1, 2, 3, 4, 5, 6, 7], toAsync, slice(2));
+
+const res13 = pipe([1, 2, 3, 4, 5, 6, 7], toAsync, slice(2, 3));
+const res14 = pipe([1, 2, 3, 4, 5, 6, 7], toAsync, slice(2, 3));
+
+checks([
+  check<typeof res1, IterableIterator<number>, Test.Pass>(),
+  check<typeof res2, IterableIterator<number>, Test.Pass>(),
+
+  check<typeof res3, IterableIterator<string>, Test.Pass>(),
+  check<typeof res4, IterableIterator<string>, Test.Pass>(),
+
+  check<typeof res5, AsyncIterableIterator<number>, Test.Pass>(),
+  check<typeof res6, AsyncIterableIterator<number>, Test.Pass>(),
+
+  check<typeof res7, IterableIterator<number>, Test.Pass>(),
+  check<typeof res8, IterableIterator<number>, Test.Pass>(),
+
+  check<typeof res9, IterableIterator<number>, Test.Pass>(),
+  check<typeof res10, IterableIterator<number>, Test.Pass>(),
+
+  check<typeof res11, AsyncIterableIterator<number>, Test.Pass>(),
+  check<typeof res12, AsyncIterableIterator<number>, Test.Pass>(),
+
+  check<typeof res13, AsyncIterableIterator<number>, Test.Pass>(),
+  check<typeof res14, AsyncIterableIterator<number>, Test.Pass>(),
+]);
diff --git a/type-check/average.test.ts b/type-check/average.test.ts
new file mode 100644
index 00000000..7a0176be
--- /dev/null
+++ b/type-check/average.test.ts
@@ -0,0 +1,16 @@
+import * as Test from "../src/types/Test";
+import { average, pipe, toAsync } from "../src";
+
+const { checks, check } = Test;
+
+const res1 = average([1, 2, 3]);
+const res2 = average(toAsync([1, 2, 3]));
+const res3 = pipe([1, 2, 3], average);
+const res4 = pipe([1, 2, 3], toAsync, average);
+
+checks([
+  check<typeof res1, number, Test.Pass>(),
+  check<typeof res2, Promise<number>, Test.Pass>(),
+  check<typeof res3, number, Test.Pass>(),
+  check<typeof res4, Promise<number>, Test.Pass>(),
+]);
diff --git a/type-check/sortBy.test.ts b/type-check/sortBy.test.ts
new file mode 100644
index 00000000..04092901
--- /dev/null
+++ b/type-check/sortBy.test.ts
@@ -0,0 +1,22 @@
+import * as Test from "../src/types/Test";
+import { identity, pipe, sortBy, toAsync } from "../src";
+
+const { checks, check } = Test;
+
+const res0 = sortBy(identity, []);
+const res1 = sortBy(identity, [1, 2, 3]);
+const res2 = sortBy(identity, "abc");
+const res3 = sortBy(identity, toAsync([1, 2, 3]));
+const res4 = sortBy(identity, toAsync("abc"));
+const res5 = pipe([1, 2, 3], sortBy(identity));
+const res6 = pipe([1, 2, 3], toAsync, sortBy(identity));
+
+checks([
+  check<typeof res0, any[], Test.Pass>(),
+  check<typeof res1, number[], Test.Pass>(),
+  check<typeof res2, string[], Test.Pass>(),
+  check<typeof res3, Promise<number[]>, Test.Pass>(),
+  check<typeof res4, Promise<string[]>, Test.Pass>(),
+  check<typeof res5, number[], Test.Pass>(),
+  check<typeof res6, Promise<number[]>, Test.Pass>(),
+]);