Repository: marpple/FxTS (ts)

Issue:
feat: change the name of takeUntil to takeUntilInclusive.
# Suggestion

<!--
  Please fill in each section completely. Thank you!
-->

## ⭐ Suggestion

<!-- What would you like to be added? -->


The existing `takeUntil` implementation in `fxts` returns the value when the predicate becomes true.
In contrast, the implementation of `takeUntil` in `rxjs` does not return the value corresponding to the true condition of the predicate. To avoid confusion with the usage of the existing `takeUntil`, we will rename `fxts`'s `takeUntil` to `takeUntilInclusive`.

https://rxjs.dev/api/index/function/takeUntil

## 💻 Use Cases

<!-- What do you want to use this for? -->




Pull request description:
feat: change `takeUntil` to `takeUntilInclusive`
Fixes #311


c.c @indongyoo 
 

Patch:
diff --git a/src/Lazy/fx.ts b/src/Lazy/fx.ts
index ac8a1a34..ebe4e532 100644
--- a/src/Lazy/fx.ts
+++ b/src/Lazy/fx.ts
@@ -26,7 +26,7 @@ import peek from "./peek";
 import reject from "./reject";
 import slice from "./slice";
 import take from "./take";
-import takeUntil from "./takeUntil";
+import takeUntilInclusive from "./takeUntilInclusive";
 import takeWhile from "./takeWhile";
 import toAsync from "./toAsync";
 
@@ -135,10 +135,20 @@ class FxAsyncIterable<A> {
   /**
    * Returns AsyncIterable that taken values until truthy when given `f` is applied.
    *
+   * @deprecated Use `takeUntilInclusive` instead of this function.
    * see {@link https://fxts.dev/docs/takeUntil | takeUntil}
    */
   takeUntil(f: (a: A) => unknown): FxAsyncIterable<A> {
-    return new FxAsyncIterable(takeUntil(f, this.asyncIterable));
+    return new FxAsyncIterable(takeUntilInclusive(f, this.asyncIterable));
+  }
+
+  /**
+   * Returns AsyncIterable that taken values until truthy when given `f` is applied.
+   *
+   * see {@link https://fxts.dev/docs/takeUntilInclusive | takeUntilInclusive}
+   */
+  takeUntilInclusive(f: (a: A) => unknown): FxAsyncIterable<A> {
+    return new FxAsyncIterable(takeUntilInclusive(f, this.asyncIterable));
   }
 
   /**
@@ -440,10 +450,20 @@ export class FxIterable<A> {
   /**
    * Returns Iterable that taken values until truthy when given `f` is applied.
    *
+   * @deprecated Use `takeUntilInclusive` instead of this function.
    * see {@link https://fxts.dev/docs/takeUntil | takeUntil}
    */
   takeUntil(f: (a: A) => unknown): FxIterable<A> {
-    return new FxIterable(takeUntil(f, this.iterable));
+    return new FxIterable(takeUntilInclusive(f, this.iterable));
+  }
+
+  /**
+   * Returns AsyncIterable that taken values until truthy when given `f` is applied.
+   *
+   * see {@link https://fxts.dev/docs/takeUntilInclusive | takeUntilInclusive}
+   */
+  takeUntilInclusive(f: (a: A) => unknown): FxIterable<A> {
+    return new FxIterable(takeUntilInclusive(f, this.iterable));
   }
 
   /**
diff --git a/src/Lazy/index.ts b/src/Lazy/index.ts
index e7a90f8a..a0093060 100644
--- a/src/Lazy/index.ts
+++ b/src/Lazy/index.ts
@@ -35,6 +35,7 @@ import split from "./split";
 import take from "./take";
 import takeRight from "./takeRight";
 import takeUntil from "./takeUntil";
+import takeUntilInclusive from "./takeUntilInclusive";
 import takeWhile from "./takeWhile";
 import toAsync from "./toAsync";
 import uniq from "./uniq";
@@ -82,6 +83,7 @@ export {
   split,
   take,
   takeUntil,
+  takeUntilInclusive,
   takeRight,
   takeWhile,
   toAsync,
diff --git a/src/Lazy/takeUntil.ts b/src/Lazy/takeUntil.ts
index 0ed5708f..753461cc 100644
--- a/src/Lazy/takeUntil.ts
+++ b/src/Lazy/takeUntil.ts
@@ -1,81 +1,13 @@
-import { AsyncFunctionException } from "../_internal/error";
-import { isAsyncIterable, isIterable, isPromise } from "../_internal/utils";
+import { isAsyncIterable, isIterable } from "../_internal/utils";
 import type IterableInfer from "../types/IterableInfer";
 import type ReturnIterableIteratorType from "../types/ReturnIterableIteratorType";
-import concurrent, { isConcurrent } from "./concurrent";
-
-function* sync<A, B>(f: (a: A) => B, iterable: Iterable<A>) {
-  for (const item of iterable) {
-    yield item;
-
-    const res = f(item);
-    if (isPromise(res)) {
-      throw new AsyncFunctionException();
-    }
-
-    if (res) {
-      break;
-    }
-  }
-}
-
-function asyncSequential<A, B>(
-  f: (a: A) => B,
-  iterable: AsyncIterable<A>,
-): AsyncIterableIterator<A> {
-  const iterator = iterable[Symbol.asyncIterator]();
-  let end = false;
-  return {
-    [Symbol.asyncIterator]() {
-      return this;
-    },
-    async next(_concurrent) {
-      if (end) {
-        return { done: true, value: undefined };
-      }
-
-      const { done, value } = await iterator.next(_concurrent);
-      if (done || end) {
-        return { done: true, value: undefined };
-      }
-
-      const cond = await f(value);
-      if (end) {
-        return { done: true, value: undefined };
-      }
-
-      if (cond) {
-        end = true;
-      }
-
-      return { done: false, value };
-    },
-  };
-}
-
-function async<A, B>(
-  f: (a: A) => B,
-  iterable: AsyncIterable<A>,
-): AsyncIterableIterator<A> {
-  let _iterator: AsyncIterator<A>;
-  return {
-    async next(_concurrent: any) {
-      if (_iterator === undefined) {
-        _iterator = isConcurrent(_concurrent)
-          ? asyncSequential(f, concurrent(_concurrent.length, iterable))
-          : asyncSequential(f, iterable);
-      }
-      return _iterator.next(_concurrent);
-    },
-    [Symbol.asyncIterator]() {
-      return this;
-    },
-  };
-}
+import takeUntilInclusive from "./takeUntilInclusive";
 
 /**
  * Returns Iterable/AsyncIterable that taken values until truthy when given `f` is applied.
  *
+ *
+ * @deprecated Use `takeUntilInclusive` instead of this function.
  * @example
  * ```ts
  * const iter = takeUntil(a => a % 2 === 0, [1, 2, 3, 4, 5, 6]);
@@ -142,16 +74,23 @@ function takeUntil<A extends Iterable<unknown> | AsyncIterable<unknown>, B>(
   | ((iterable: A) => ReturnIterableIteratorType<A>) {
   if (iterable === undefined) {
     return (iterable: A) => {
-      return takeUntil(f, iterable as any) as ReturnIterableIteratorType<A>;
+      return takeUntilInclusive(
+        f,
+        iterable as any,
+      ) as ReturnIterableIteratorType<A>;
     };
   }
 
   if (isIterable(iterable)) {
-    return sync(f, iterable) as IterableIterator<IterableInfer<A>>;
+    return takeUntilInclusive(f, iterable) as IterableIterator<
+      IterableInfer<A>
+    >;
   }
 
   if (isAsyncIterable(iterable)) {
-    return async(f, iterable) as AsyncIterableIterator<IterableInfer<A>>;
+    return takeUntilInclusive(f, iterable) as AsyncIterableIterator<
+      IterableInfer<A>
+    >;
   }
 
   throw new TypeError("'iterable' must be type of Iterable or AsyncIterable");
diff --git a/src/Lazy/takeUntilInclusive.ts b/src/Lazy/takeUntilInclusive.ts
new file mode 100644
index 00000000..4cc82cc4
--- /dev/null
+++ b/src/Lazy/takeUntilInclusive.ts
@@ -0,0 +1,169 @@
+import { AsyncFunctionException } from "../_internal/error";
+import { isAsyncIterable, isIterable, isPromise } from "../_internal/utils";
+import type IterableInfer from "../types/IterableInfer";
+import type ReturnIterableIteratorType from "../types/ReturnIterableIteratorType";
+import concurrent, { isConcurrent } from "./concurrent";
+
+function* sync<A, B>(f: (a: A) => B, iterable: Iterable<A>) {
+  for (const item of iterable) {
+    yield item;
+
+    const res = f(item);
+    if (isPromise(res)) {
+      throw new AsyncFunctionException();
+    }
+
+    if (res) {
+      break;
+    }
+  }
+}
+
+function asyncSequential<A, B>(
+  f: (a: A) => B,
+  iterable: AsyncIterable<A>,
+): AsyncIterableIterator<A> {
+  const iterator = iterable[Symbol.asyncIterator]();
+  let end = false;
+  return {
+    [Symbol.asyncIterator]() {
+      return this;
+    },
+    async next(_concurrent) {
+      if (end) {
+        return { done: true, value: undefined };
+      }
+
+      const { done, value } = await iterator.next(_concurrent);
+      if (done || end) {
+        return { done: true, value: undefined };
+      }
+
+      const cond = await f(value);
+      if (end) {
+        return { done: true, value: undefined };
+      }
+
+      if (cond) {
+        end = true;
+      }
+
+      return { done: false, value };
+    },
+  };
+}
+
+function async<A, B>(
+  f: (a: A) => B,
+  iterable: AsyncIterable<A>,
+): AsyncIterableIterator<A> {
+  let _iterator: AsyncIterator<A>;
+  return {
+    async next(_concurrent: any) {
+      if (_iterator === undefined) {
+        _iterator = isConcurrent(_concurrent)
+          ? asyncSequential(f, concurrent(_concurrent.length, iterable))
+          : asyncSequential(f, iterable);
+      }
+      return _iterator.next(_concurrent);
+    },
+    [Symbol.asyncIterator]() {
+      return this;
+    },
+  };
+}
+
+/**
+ * Returns Iterable/AsyncIterable that taken values until truthy when given `f` is applied.
+ *
+ * @example
+ * ```ts
+ * const iter = takeUntilInclusive(a => a % 2 === 0, [1, 2, 3, 4, 5, 6]);
+ * iter.next() // {done:false, value: 1}
+ * iter.next() // {done:false, value: 2}
+ * iter.next() // {done:true, value: undefined}
+ *
+ * // with pipe
+ * pipe(
+ *  [1, 2, 3, 4, 5, 6],
+ *  takeUntilInclusive(a => a % 2 === 0),
+ *  toArray,
+ * ); // [1, 2]
+ *
+ * await pipe(
+ *  Promise.resolve([1, 2, 3, 4, 5, 6]),
+ *  takeUntilInclusive(a => a % 2 === 0),
+ *  toArray,
+ * ); // [1, 2]
+ *
+ * // if you want to use asynchronous callback
+ * await pipe(
+ *  Promise.resolve([1, 2, 3, 4, 5, 6]),
+ *  toAsync,
+ *  takeUntilInclusive(async (a) => a % 2 === 0),
+ *  toArray,
+ * ); // [1, 2]
+ *
+ * // with toAsync
+ * await pipe(
+ *  [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3),
+ * Promise.resolve(4), Promise.resolve(5), Promise.resolve(6)],
+ *  toAsync,
+ *  takeUntilInclusive(a => a % 2 === 0),
+ *  toArray,
+ * ); // [1, 2]
+ * ```
+ *
+ * {@link https://codesandbox.io/s/fxts-takeuntil-gv1jk | Try It}
+ *
+ * see {@link https://fxts.dev/docs/pipe | pipe}, {@link https://fxts.dev/docs/toAsync | toAsync},
+ * {@link https://fxts.dev/docs/toArray | toArray}
+ */
+function takeUntilInclusive<A, B>(
+  f: (a: A) => B,
+  iterable: Iterable<A>,
+): IterableIterator<A>;
+
+function takeUntilInclusive<A, B>(
+  f: (a: A) => B,
+  iterable: AsyncIterable<A>,
+): AsyncIterableIterator<A>;
+
+function takeUntilInclusive<
+  A extends Iterable<unknown> | AsyncIterable<unknown>,
+  B,
+>(
+  f: (a: IterableInfer<A>) => B,
+): (iterable: A) => ReturnIterableIteratorType<A>;
+
+function takeUntilInclusive<
+  A extends Iterable<unknown> | AsyncIterable<unknown>,
+  B,
+>(
+  f: (a: A) => B,
+  iterable?: Iterable<A> | AsyncIterable<A>,
+):
+  | IterableIterator<IterableInfer<A>>
+  | AsyncIterableIterator<IterableInfer<A>>
+  | ((iterable: A) => ReturnIterableIteratorType<A>) {
+  if (iterable === undefined) {
+    return (iterable: A) => {
+      return takeUntilInclusive(
+        f,
+        iterable as any,
+      ) as ReturnIterableIteratorType<A>;
+    };
+  }
+
+  if (isIterable(iterable)) {
+    return sync(f, iterable) as IterableIterator<IterableInfer<A>>;
+  }
+
+  if (isAsyncIterable(iterable)) {
+    return async(f, iterable) as AsyncIterableIterator<IterableInfer<A>>;
+  }
+
+  throw new TypeError("'iterable' must be type of Iterable or AsyncIterable");
+}
+
+export default takeUntilInclusive;
diff --git a/src/every.ts b/src/every.ts
index 6d5859c9..3b71ee8b 100644
--- a/src/every.ts
+++ b/src/every.ts
@@ -1,7 +1,7 @@
 import { throwIfPromiseError } from "./_internal/error";
 import { isAsyncIterable, isIterable } from "./_internal/utils";
 import map from "./Lazy/map";
-import takeUntil from "./Lazy/takeUntil";
+import takeUntilInclusive from "./Lazy/takeUntilInclusive";
 import not from "./not";
 import pipe from "./pipe";
 import reduce from "./reduce";
@@ -58,7 +58,7 @@ function every<
   if (isIterable<IterableInfer<A>>(iterable)) {
     return pipe(
       map((a) => throwIfPromiseError(f(a)), iterable),
-      takeUntil(not),
+      takeUntilInclusive(not),
       (acc) =>
         reduce(
           (a: boolean, b: boolean) => a && b,
@@ -73,7 +73,7 @@ function every<
   if (isAsyncIterable<IterableInfer<A>>(iterable)) {
     return pipe(
       map(f, iterable),
-      takeUntil(not),
+      takeUntilInclusive(not),
       (acc) =>
         reduce(
           (a: boolean, b: boolean) => a && b,
diff --git a/src/index.ts b/src/index.ts
index 829a7397..7921b402 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -64,6 +64,7 @@ import throwIf from "./throwIf";
 import toArray from "./toArray";
 import unicodeToArray from "./unicodeToArray";
 import unless from "./unless";
+import when from "./when";
 
 export {
   add,
@@ -136,6 +137,7 @@ export {
   throwIf,
   toArray,
   unicodeToArray,
+  when,
 };
 
 export * from "./Lazy/index";
diff --git a/src/some.ts b/src/some.ts
index 46dfc016..e7231ff7 100644
--- a/src/some.ts
+++ b/src/some.ts
@@ -2,7 +2,7 @@ import { throwIfPromiseError } from "./_internal/error";
 import { isAsyncIterable, isIterable } from "./_internal/utils";
 import identity from "./identity";
 import map from "./Lazy/map";
-import takeUntil from "./Lazy/takeUntil";
+import takeUntilInclusive from "./Lazy/takeUntilInclusive";
 import pipe from "./pipe";
 import reduce from "./reduce";
 import type Arrow from "./types/Arrow";
@@ -78,7 +78,7 @@ function some<
   if (isIterable<IterableInfer<A>>(iterable)) {
     return pipe(
       map((a) => throwIfPromiseError(f(a)), iterable),
-      takeUntil(identity),
+      takeUntilInclusive(identity),
       (acc) =>
         reduce(
           (a: boolean, b: boolean) => a || b,
@@ -92,7 +92,7 @@ function some<
   if (isAsyncIterable<IterableInfer<A>>(iterable)) {
     return pipe(
       map(f, iterable),
-      takeUntil(identity),
+      takeUntilInclusive(identity),
       (acc) =>
         reduce(
           (a: boolean, b: boolean) => a || b,
diff --git a/src/when.ts b/src/when.ts
new file mode 100644
index 00000000..fffdff27
--- /dev/null
+++ b/src/when.ts
@@ -0,0 +1,75 @@
+import isUndefined from "./isUndefined";
+
+/**
+ * It returns the original value based on the condition of the first argument or the result of executing the function passed as the second argument.
+ *
+ * @example
+ * ```typescript
+ * when(
+ *   isNumber,
+ *   () => `This is number`
+ *   100,
+ * ); // This is number
+ * ```
+ *
+ * with pipe
+ *
+ * ```typescript
+ * pipe(
+ *   100,
+ *   when(
+ *     isNumber,
+ *     () => `This is number`
+ *   )
+ * ) // This is number
+ *
+ * pipe(
+ *   100,
+ *   when(
+ *     isNumber,
+ *     (value) => value * 2
+ *   )
+ * ) // 200
+ *
+ * pipe(
+ *   100,
+ *   when(
+ *     isString,
+ *     () => `This is number` // not work
+ *   ),
+ * ) // 100
+ * ```
+ */
+
+function when<T, S extends T, R>(
+  predicate: (input: T) => input is S,
+  callback: (input: S) => R,
+): (iterator: T) => Exclude<T, S>;
+function when<T, R>(
+  predicate: (input: T) => boolean,
+  callback: (input: T) => R,
+): (iterator: T) => T | R;
+function when<T, S extends T, R>(
+  predicate: (input: T) => input is S,
+  callback: (input: S) => R,
+  iterator: T,
+): Exclude<T, S>;
+function when<T, R>(
+  predicate: (input: T) => boolean,
+  callback: (input: T) => R,
+  iterator: T,
+): T | R;
+
+function when<T, R>(
+  predicate: (input: T) => boolean,
+  callback: (input: T) => R,
+  iterator?: T,
+) {
+  if (isUndefined(iterator))
+    return (currentIterator: T) => when(predicate, callback, currentIterator);
+  if (predicate(iterator)) return callback(iterator);
+
+  return iterator;
+}
+
+export default when;
diff --git a/website/function.json b/website/function.json
index ed8b5796..802aae09 100644
--- a/website/function.json
+++ b/website/function.json
@@ -36,7 +36,7 @@
     "split",
     "take",
     "takeRight",
-    "takeUntil",
+    "takeUntilInclusive",
     "takeWhile",
     "toAsync",
     "uniq",
diff --git a/website/markdown-plugin/src/function.json b/website/markdown-plugin/src/function.json
index ed8b5796..802aae09 100644
--- a/website/markdown-plugin/src/function.json
+++ b/website/markdown-plugin/src/function.json
@@ -36,7 +36,7 @@
     "split",
     "take",
     "takeRight",
-    "takeUntil",
+    "takeUntilInclusive",
     "takeWhile",
     "toAsync",
     "uniq",


Test patch:
diff --git a/test/Lazy/takeUntil.spec.ts b/test/Lazy/takeUntilInclusive.spec.ts
similarity index 79%
rename from test/Lazy/takeUntil.spec.ts
rename to test/Lazy/takeUntilInclusive.spec.ts
index a1720b2c..f91a2686 100644
--- a/test/Lazy/takeUntil.spec.ts
+++ b/test/Lazy/takeUntilInclusive.spec.ts
@@ -8,31 +8,35 @@ import {
   peek,
   pipe,
   range,
-  takeUntil,
+  takeUntilInclusive,
   toArray,
   toAsync,
 } from "../../src/index";
 import { Concurrent } from "../../src/Lazy/concurrent";
 import { generatorMock } from "../utils";
 
-describe("takeUntil", function () {
+describe("takeUntilInclusive", function () {
   describe("sync", function () {
     it("should be able to take the element until the callback result is truthy", function () {
       const res = [];
-      for (const item of takeUntil((a) => a % 2 === 0, [1, 2, 3, 4])) {
+      for (const item of takeUntilInclusive((a) => a % 2 === 0, [1, 2, 3, 4])) {
         res.push(item);
       }
       expect(res).toEqual([1, 2]);
 
-      const res1 = toArray(takeUntil((a) => a % 2 === 0, [1, 2, 3, 4]));
+      const res1 = toArray(
+        takeUntilInclusive((a) => a % 2 === 0, [1, 2, 3, 4]),
+      );
       expect(res1).toEqual([1, 2]);
 
-      const res2 = toArray(takeUntil((a) => a > 5, [1, 2, 3, 4]));
+      const res2 = toArray(takeUntilInclusive((a) => a > 5, [1, 2, 3, 4]));
       expect(res2).toEqual([1, 2, 3, 4]);
     });
 
     it("should throw an erorr when the callback is asynchronous", function () {
-      const res = () => [...takeUntil(async (a) => a % 2 === 0, [1, 2, 3, 4])];
+      const res = () => [
+        ...takeUntilInclusive(async (a) => a % 2 === 0, [1, 2, 3, 4]),
+      ];
       expect(res).toThrowError(new AsyncFunctionException());
     });
 
@@ -41,7 +45,7 @@ describe("takeUntil", function () {
         [1, 2, 3, 4],
         map((a) => a + 10),
         filter((a) => a % 2 === 0),
-        takeUntil((a) => a > 12),
+        takeUntilInclusive((a) => a > 12),
         toArray,
       );
 
@@ -52,7 +56,7 @@ describe("takeUntil", function () {
       const res = fx([1, 2, 3, 4])
         .map((a) => a + 10)
         .filter((a) => a % 2 === 0)
-        .takeUntil((a) => a > 12)
+        .takeUntilInclusive((a) => a > 12)
         .toArray();
 
       expect(res).toEqual([12, 14]);
@@ -63,17 +67,17 @@ describe("takeUntil", function () {
     it("should be able to take the element until the callback result is truthy", async function () {
       const res = [];
       // prettier-ignore
-      for await (const item of takeUntil((a) => a % 2 === 0, toAsync([1, 2, 3, 4]))) {
+      for await (const item of takeUntilInclusive((a) => a % 2 === 0, toAsync([1, 2, 3, 4]))) {
       res.push(item);
     }
       expect(res).toEqual([1, 2]);
 
       // prettier-ignore
-      const res1 = await toArray(takeUntil((a) => a % 2 === 0, toAsync([1, 2, 3, 4])));
+      const res1 = await toArray(takeUntilInclusive((a) => a % 2 === 0, toAsync([1, 2, 3, 4])));
       expect(res1).toEqual([1, 2]);
 
       const res2 = await toArray(
-        takeUntil((a) => a > 5, toAsync([1, 2, 3, 4])),
+        takeUntilInclusive((a) => a > 5, toAsync([1, 2, 3, 4])),
       );
       expect(res2).toEqual([1, 2, 3, 4]);
     });
@@ -81,17 +85,17 @@ describe("takeUntil", function () {
     it("should be able to take the element until the async callback result is truthy", async function () {
       const res = [];
       // prettier-ignore
-      for await (const item of takeUntil((a) => a % 2 === 0, toAsync([1, 2, 3, 4]))) {
+      for await (const item of takeUntilInclusive((a) => a % 2 === 0, toAsync([1, 2, 3, 4]))) {
       res.push(item);
     }
       expect(res).toEqual([1, 2]);
 
       // prettier-ignore
-      const res1 = await toArray(takeUntil(async(a) => a % 2 === 0, toAsync([1, 2, 3, 4])));
+      const res1 = await toArray(takeUntilInclusive(async(a) => a % 2 === 0, toAsync([1, 2, 3, 4])));
       expect(res1).toEqual([1, 2]);
 
       // prettier-ignore
-      const res2 = await toArray(takeUntil(async(a) => a > 5, toAsync([1, 2, 3, 4])));
+      const res2 = await toArray(takeUntilInclusive(async(a) => a > 5, toAsync([1, 2, 3, 4])));
       expect(res2).toEqual([1, 2, 3, 4]);
     });
 
@@ -100,7 +104,7 @@ describe("takeUntil", function () {
         toAsync([1, 2, 3, 4]),
         map((a) => a + 10),
         filter((a) => a % 2 === 0),
-        takeUntil((a) => a > 12),
+        takeUntilInclusive((a) => a > 12),
         toArray,
       );
 
@@ -111,7 +115,7 @@ describe("takeUntil", function () {
       const res = await fx(toAsync([1, 2, 3, 4]))
         .map((a) => a + 10)
         .filter((a) => a % 2 === 0)
-        .takeUntil((a) => a > 12)
+        .takeUntilInclusive((a) => a > 12)
         .toArray();
 
       expect(res).toEqual([12, 14]);
@@ -123,7 +127,7 @@ describe("takeUntil", function () {
         toAsync([3, 3, 1, 0, 0]),
         peek(fn),
         map((a) => delay(100, a)),
-        takeUntil((a) => a < 3),
+        takeUntilInclusive((a) => a < 3),
         toArray,
       );
 
@@ -134,7 +138,7 @@ describe("takeUntil", function () {
     it("should be able to take the element until the callback result is truthy concurrently", async function () {
       const res = await pipe(
         toAsync(range(1, 20)),
-        takeUntil((a) => a > 12),
+        takeUntilInclusive((a) => a > 12),
         concurrent(4),
         toArray,
       );
@@ -146,7 +150,7 @@ describe("takeUntil", function () {
       const res = pipe(
         toAsync(range(1, 500)),
         map((a) => delay(100, a + 10)),
-        takeUntil((a) => a > 14),
+        takeUntilInclusive((a) => a > 14),
         concurrent(2),
       );
 
@@ -166,7 +170,7 @@ describe("takeUntil", function () {
         toAsync(range(1, 500)),
         map((a) => delay(100, a + 10)),
         filter((a) => a % 2 === 0),
-        takeUntil((a) => a > 20),
+        takeUntilInclusive((a) => a > 20),
         concurrent(2),
         toArray,
       );
@@ -175,7 +179,7 @@ describe("takeUntil", function () {
 
     it("should be passed concurrent object when job works concurrently", async function () {
       const mock = generatorMock();
-      const iter = takeUntil((a) => a, mock);
+      const iter = takeUntilInclusive((a) => a, mock);
       const concurrent = Concurrent.of(2) as any;
 
       await iter.next(concurrent);
@@ -189,7 +193,7 @@ describe("takeUntil", function () {
           toAsync,
           map((a) => delay(100, a + 10)),
           filter((a) => a % 2 === 0),
-          takeUntil((a) => {
+          takeUntilInclusive((a) => {
             if (a > 15) {
               throw new Error("err");
             }
@@ -216,7 +220,7 @@ describe("takeUntil", function () {
             yield delay(200, 9);
           })(),
         ),
-        takeUntil((a) => a > 8),
+        takeUntilInclusive((a) => a > 8),
         concurrent(5),
         toArray,
       );
diff --git a/test/when.spec.ts b/test/when.spec.ts
new file mode 100644
index 00000000..1f606a74
--- /dev/null
+++ b/test/when.spec.ts
@@ -0,0 +1,37 @@
+import { isString, pipe, when } from "../src";
+
+describe("when", function () {
+  const test = <T>(value: T) =>
+    when(
+      (value) => value === 100,
+      (value) => {
+        expect(value).toBeTruthy();
+      },
+      value,
+    );
+  const withPipe = <T>(value: T) =>
+    pipe(
+      value,
+      test,
+      when(isString, () => "Hello fxts"),
+    );
+
+  it("If the input value is a number, it will be filtered out by the first 'when' function. And throw undefined.", () => {
+    const INPUT_VALUE = 100;
+
+    test(INPUT_VALUE);
+    withPipe(INPUT_VALUE);
+  });
+  it("If the input value is a string, it will be filtered out by the second 'when' function. And return value is 'Hello fxts'", () => {
+    const INPUT_VALUE = "Hello World";
+
+    expect(test(INPUT_VALUE)).toBe("Hello World");
+    expect(withPipe(INPUT_VALUE)).toBe("Hello fxts");
+  });
+  it("If nothing matches, all 'when' functions will be passed.", () => {
+    const INPUT_VALUE = [1, 2, 3, 4];
+
+    expect(test(INPUT_VALUE)).toEqual(INPUT_VALUE);
+    expect(withPipe(INPUT_VALUE)).toEqual(INPUT_VALUE);
+  });
+});
diff --git a/type-check/Lazy/takeUntil.test.ts b/type-check/Lazy/takeUntilInclusive.test.ts
similarity index 62%
rename from type-check/Lazy/takeUntil.test.ts
rename to type-check/Lazy/takeUntilInclusive.test.ts
index b76a0842..170c816f 100644
--- a/type-check/Lazy/takeUntil.test.ts
+++ b/type-check/Lazy/takeUntilInclusive.test.ts
@@ -1,28 +1,28 @@
-import { pipe, takeUntil, toAsync } from "../../src";
+import { pipe, takeUntilInclusive, toAsync } from "../../src";
 import * as Test from "../../src/types/Test";
 
 const { checks, check } = Test;
 
-const res1 = takeUntil((a) => a > 2, [1, 2, 3]);
-const res2 = takeUntil(async (a) => a > 2, [1, 2, 3]);
-const res3 = takeUntil((a) => a > 2, toAsync([1, 2, 3]));
-const res4 = takeUntil(async (a) => a > 2, toAsync([1, 2, 3]));
+const res1 = takeUntilInclusive((a) => a > 2, [1, 2, 3]);
+const res2 = takeUntilInclusive(async (a) => a > 2, [1, 2, 3]);
+const res3 = takeUntilInclusive((a) => a > 2, toAsync([1, 2, 3]));
+const res4 = takeUntilInclusive(async (a) => a > 2, toAsync([1, 2, 3]));
 
 const res5 = pipe(
   [1, 2, 3, 4],
-  takeUntil((a) => a > 2),
+  takeUntilInclusive((a) => a > 2),
 );
 const res6 = pipe(
   [1, 2, 3, 4],
-  takeUntil(async (a) => a > 2),
+  takeUntilInclusive(async (a) => a > 2),
 );
 const res7 = pipe(
   toAsync([1, 2, 3, 4]),
-  takeUntil((a) => a > 2),
+  takeUntilInclusive((a) => a > 2),
 );
 const res8 = pipe(
   toAsync([1, 2, 3, 4]),
-  takeUntil(async (a) => a > 2),
+  takeUntilInclusive(async (a) => a > 2),
 );
 
 checks([
diff --git a/type-check/when.test.ts b/type-check/when.test.ts
new file mode 100644
index 00000000..a96d61ce
--- /dev/null
+++ b/type-check/when.test.ts
@@ -0,0 +1,28 @@
+import { isNumber, isString, sum, when } from "../src";
+import * as Test from "../src/types/Test";
+
+const { checks, check } = Test;
+
+const VALUE_1 = 100;
+const VALUE_2 = [1, 2, 3, 4];
+
+const check1 = when(isNumber, (value) => `${value}`, VALUE_1);
+const check2 = when(isString, (value) => `${value}`, VALUE_1);
+const check3 = when(
+  (list) => sum(list) >= 10,
+  (value) => `${value}`,
+  VALUE_2,
+);
+const check4 = when(
+  (list) => sum(list) > 10,
+  (value) => `${value}`,
+  VALUE_2,
+);
+
+checks([
+  check<typeof VALUE_1, 100, Test.Pass>(),
+  check<typeof check1, string, Test.Pass>(),
+  check<typeof check2, number, Test.Pass>(),
+  check<typeof check3, typeof VALUE_2 | string, Test.Pass>(),
+  check<typeof check4, typeof VALUE_2 | string, Test.Pass>(),
+]);