Repository: marpple/FxTS (ts)

Issue:
feat: support fluent Interface
# Suggestion

<!--
  Please fill in each section completely. Thank you!
-->

## ⭐ Suggestion

<!-- What would you like to be added? -->

Supports method chaining only for commonly used functions.
The functions that will be provided first are as follows.


- map
- flatMap
- flat
- filter
- reject
- reduce 
- take
- takeWhile
- takeUntil
- peek 
- groupBy 
- indexBy 
- drop 
- some 
- every
- find 
- toArray
- each

- consume
- toAsync
- concurrent 
 



https://en.wikipedia.org/wiki/Fluent_interface


## 💻 Use Cases

<!-- What do you want to use this for? -->

```ts
const result = Fx([1,2,3])
.map(a=>a.toFixed())
.toArray()
```



Functions not included in the method will be made available through `apply`.

```ts
const result = Fx([1,2,3])
.map(a=>a.toFixed())
.apply(take(1))
.toArray()
```


Pull request:
feat: add `fx`
Fixes #250 


Supports method chaining only for commonly used functions.

- [x] add function `fx`
- [x] add test case
  - [x] type
  - [x] logic
- [x] guide


Patch:
diff --git a/README.md b/README.md
index e58915d0..25dad6cc 100644
--- a/README.md
+++ b/README.md
@@ -25,7 +25,7 @@ Please review the [API documentation](https://fxts.dev/docs/index)
 ## Usage
 
 ```ts
-import { each, filter, map, pipe, range, take } from "@fxts/core";
+import { each, filter, fx, map, pipe, range, take } from "@fxts/core";
 
 pipe(
   range(10),
@@ -34,18 +34,25 @@ pipe(
   take(2),
   each((a) => console.log(a)),
 );
+
+// chaining
+fx(range(10))
+  .map((a) => a + 10)
+  .filter((a) => a % 2 === 0)
+  .take(2)
+  .each((a) => console.log(a));
 ```
 
 ## Usage(concurrent)
 
 ```ts
-import { concurrent, countBy, flat, map, pipe, toAsync } from "@fxts/core";
+import { concurrent, countBy, flat, fx, map, pipe, toAsync } from "@fxts/core";
 
 // maybe 1 seconds api
 const fetchWiki = (page: string) =>
   fetch(`https://en.wikipedia.org/w/api.php?action=parse&page=${page}`);
 
-const countWords = async (concurrency = 1) =>
+const countWords = async (concurrency: number) =>
   pipe(
     ["html", "css", "javascript", "typescript"],
     toAsync,
diff --git a/src/Lazy/fx.ts b/src/Lazy/fx.ts
new file mode 100644
index 00000000..d2d8cae7
--- /dev/null
+++ b/src/Lazy/fx.ts
@@ -0,0 +1,567 @@
+import { isAsyncIterable, isIterable } from "../_internal/utils";
+import consume from "../consume";
+import each from "../each";
+import every from "../every";
+import find from "../find";
+import findIndex from "../findIndex";
+import groupBy from "../groupBy";
+import indexBy from "../indexBy";
+import isUndefined from "../isUndefined";
+import join from "../join";
+import reduce from "../reduce";
+import some from "../some";
+import type Cast from "../types/Cast";
+import type IterableInfer from "../types/IterableInfer";
+import type Key from "../types/Key";
+import type { SyncReducer } from "../types/Reducer";
+import concurrent from "./concurrent";
+import drop from "./drop";
+import filter from "./filter";
+import flat from "./flat";
+import flatMap from "./flatMap";
+import map from "./map";
+import peek from "./peek";
+import reject from "./reject";
+import slice from "./slice";
+import take from "./take";
+import takeUntil from "./takeUntil";
+import takeWhile from "./takeWhile";
+import toAsync from "./toAsync";
+
+class FxAsyncIterable<A> {
+  private asyncIterable: AsyncIterable<A>;
+
+  constructor(asyncIterable: AsyncIterable<A>) {
+    this.asyncIterable = asyncIterable;
+  }
+
+  private [Symbol.asyncIterator]() {
+    return this.asyncIterable;
+  }
+
+  /**
+   * Returns AsyncIterable of values by running each applying `f`.
+   *
+   * see {@link https://fxts.dev/docs/map | map}
+   */
+  map<B>(f: (a: A) => B) {
+    return new FxAsyncIterable(map(f, this.asyncIterable));
+  }
+
+  /**
+   * Returns flattened AsyncIterable of values by running each element
+   * flattening the mapped results.
+   *
+   * see {@link https://fxts.dev/docs/flatMap | flatMap}
+   */
+  flatMap<B>(f: (a: A) => B) {
+    return new FxAsyncIterable(flatMap(f, this.asyncIterable));
+  }
+
+  /**
+   * Returns flattened AsyncIterable.
+   * If first argument is number, more perform flatten `flat(2, [[[1,2]]]) // [1,2]`
+   *
+   * see {@link https://fxts.dev/docs/flat | flat}
+   */
+  flat(depth?: number) {
+    return new FxAsyncIterable(flat(this.asyncIterable, depth));
+  }
+
+  /**
+   * Return AsyncIterable of all elements `f` returns truthy for
+   *
+   * see {@link https://fxts.dev/docs/filter | filter}
+   */
+  filter(f: (a: A) => unknown): FxAsyncIterable<A> {
+    return new FxAsyncIterable(filter(f, this.asyncIterable));
+  }
+
+  /**
+   * The opposite of filter method
+   * AsyncIterable of all elements `f` returns falsy for
+   *
+   * see {@link https://fxts.dev/docs/reject | reject}
+   */
+  reject(f: (a: A) => unknown): FxAsyncIterable<A> {
+    return new FxAsyncIterable(reject(f, this.asyncIterable));
+  }
+
+  /**
+   * Returns AsyncIterable that taken the first argument `l` values from asyncIterable
+   *
+   * see {@link https://fxts.dev/docs/take | take}
+   */
+  take(n: number): FxAsyncIterable<A> {
+    return new FxAsyncIterable(take(n, this.asyncIterable));
+  }
+
+  /**
+   * Returns AsyncIterable that taken values until truthy when given `f` is applied.
+   *
+   * see {@link https://fxts.dev/docs/takeUntil | takeUntil}
+   */
+  takeUntil(f: (a: A) => unknown): FxAsyncIterable<A> {
+    return new FxAsyncIterable(takeUntil(f, this.asyncIterable));
+  }
+
+  /**
+   * Returns AsyncIterable that taken values as long as each value satisfies the give `f`.
+   *
+   * see {@link https://fxts.dev/docs/takeWhile | takeWhile}
+   */
+  takeWhile(f: (a: A) => unknown): FxAsyncIterable<A> {
+    return new FxAsyncIterable(takeWhile(f, this.asyncIterable));
+  }
+
+  /**
+   * Iterate over an input list,
+   * calling a provided `f` for each element in the AsyncIterable.
+   *
+   * see {@link https://fxts.dev/docs/peek | peek}
+   */
+  peek(f: (a: A) => unknown): FxAsyncIterable<A> {
+    return new FxAsyncIterable(peek(f, this.asyncIterable));
+  }
+
+  /**
+   * Returns all but the first `length` elements of the given asyncIterable.
+   *
+   * see {@link https://fxts.dev/docs/drop | drop}
+   */
+  drop(length: number): FxAsyncIterable<A> {
+    return new FxAsyncIterable(drop(length, this.asyncIterable));
+  }
+
+  /**
+   * Returns AsyncIterable of the given elements from startIndex(inclusive) to endIndex(exclusive).
+   *
+   * see {@link https://fxts.dev/docs/slice | slice}
+   */
+  slice(start: number, end?: number): FxAsyncIterable<A> {
+    return isUndefined(end)
+      ? new FxAsyncIterable(slice(start, this.asyncIterable))
+      : new FxAsyncIterable(slice(start, end, this.asyncIterable));
+  }
+
+  /**
+   *
+   * `chain` allows you to use functions that are not provided in method chaining.
+   * The functions available for the `chain` argument return an iterable.
+   *
+   * @example
+   * ```
+   * await fx(toAsync(range(1, 4)))
+   *   .chain(append(4))
+   *   .chain(append(5))
+   *   .toArray(); // [1, 2, 3, 4, 5]
+   * ```
+   */
+  chain<B>(
+    f: (asyncIterable: AsyncIterable<A>) => AsyncIterable<Awaited<B>>,
+  ): FxAsyncIterable<B> {
+    return new FxAsyncIterable(f(this.asyncIterable));
+  }
+
+  /**
+   * Concurrent is used to balance the load of multiple asynchronous requests.
+   * The first argument receives a number that controls the number of loads, and the second argument is an AsyncIterable.
+   *
+   * see {@link https://fxts.dev/docs/concurrent | concurrent}
+   */
+  concurrent(length: number) {
+    return new FxAsyncIterable(concurrent(length, this.asyncIterable));
+  }
+
+  async consume() {
+    return consume(this.asyncIterable);
+  }
+
+  /**
+   * Splits AsyncIterable into sets, grouped by the result of running each value through `f`.
+   *
+   * see {@link https://fxts.dev/docs/groupBy | groupBy}
+   */
+  async groupBy(f: (a: A) => Key) {
+    return groupBy(f, this.asyncIterable);
+  }
+
+  /**
+   * Given `f` that generates a key,
+   * turns a list of objects into an object indexing the objects by the given key.
+   * Note that if multiple objects generate the same value for the indexing key only the last value will be included in the generated object.
+   *
+   * see {@link https://fxts.dev/docs/indexBy | indexBy}
+   */
+  async indexBy(f: (a: A) => Key) {
+    return indexBy(f, this.asyncIterable);
+  }
+
+  /**
+   * Returns true if any of the values in AsyncIterable pass `f` truth test
+   *
+   * see {@link https://fxts.dev/docs/some | some}
+   */
+  async some(f: (a: A) => unknown): Promise<boolean> {
+    return some(f, this.asyncIterable);
+  }
+
+  /**
+   * Returns true if all of the values in AsyncIterable pass the `f` truth test.
+   *
+   * see {@link https://fxts.dev/docs/every | every}
+   */
+  async every(f: (a: A) => unknown): Promise<boolean> {
+    return every(f, this.asyncIterable);
+  }
+
+  /**
+   * Returns all elements in the given iterable into a string separated by separator.
+   *
+   * see {@link https://fxts.dev/docs/join | join}
+   */
+  async join(sep: string): Promise<string> {
+    return join(sep, this.asyncIterable);
+  }
+
+  /**
+   * Looks through each value in AsyncIterable, returning the first one that passes a truth test `f`,
+   * or `undefined` if no value passes the test.
+   *
+   * see {@link https://fxts.dev/docs/find | find}
+   */
+  async find(f: (a: A) => unknown): Promise<A | undefined> {
+    return find(f, this.asyncIterable);
+  }
+
+  /**
+   * Returns the index of the first element of AsyncIterable which matches f, or -1 if no element matches.
+   *
+   * see {@link https://fxts.dev/docs/findIndex | findIndex}
+   */
+  async findIndex(f: (a: A) => unknown): Promise<number> {
+    return findIndex(f, this.asyncIterable);
+  }
+
+  /**
+   * Also known as foldl, this method boils down a list of values into a single value.
+   *
+   * see {@link https://fxts.dev/docs/reduce | reduce}
+   */
+  async reduce<B>(
+    f: SyncReducer<Cast<A, B>, A>,
+    seed?: B,
+  ): Promise<Cast<A, B>> {
+    return isUndefined(seed)
+      ? reduce(f, this.asyncIterable)
+      : reduce(f, seed as any, this.asyncIterable);
+  }
+
+  /**
+   * Iterates over AsyncIterable, applying each in turn to `f`.
+   *
+   * see {@link https://fxts.dev/docs/each | each}
+   */
+  async each(f: (a: A) => unknown): Promise<void> {
+    return each(f, this.asyncIterable);
+  }
+
+  /**
+   * Takes item from AsyncIterable and returns an array.
+   *
+   * see {@link https://fxts.dev/docs/toArray | toArray}
+   */
+  async toArray(): Promise<Array<Awaited<A>>> {
+    const array: Awaited<A>[] = [];
+    for await (const a of this.asyncIterable) {
+      array.push(a);
+    }
+    return array;
+  }
+}
+
+export class FxIterable<A> {
+  private iterable: Iterable<A>;
+
+  constructor(iterable: Iterable<A>) {
+    this.iterable = iterable;
+  }
+
+  private [Symbol.iterator]() {
+    return this.iterable;
+  }
+
+  /**
+   * Returns Iterable of values by running each applying `f`.
+   *
+   * see {@link https://fxts.dev/docs/map | map}
+   */
+  map<B>(f: (a: A) => B): FxIterable<B> {
+    return new FxIterable(map(f, this.iterable));
+  }
+
+  /**
+   * Returns flattened Iterable of values by running each element
+   * flattening the mapped results.
+   *
+   * see {@link https://fxts.dev/docs/flatMap | flatMap}
+   */
+  flatMap<B>(f: (a: A) => B) {
+    return new FxIterable(flatMap(f, this.iterable));
+  }
+
+  /**
+   * Returns flattened Iterable.
+   * If first argument is number, more perform flatten `flat(2, [[[1,2]]]) // [1,2]`
+   *
+   * see {@link https://fxts.dev/docs/flat | flat}
+   */
+  flat(depth?: number) {
+    return new FxIterable(flat(this.iterable, depth));
+  }
+
+  /**
+   * Return Iterable of all elements `f` returns truthy for
+   *
+   * see {@link https://fxts.dev/docs/filter | filter}
+   */
+  filter(f: (a: A) => unknown): FxIterable<A> {
+    return new FxIterable(filter(f, this.iterable));
+  }
+
+  /**
+   * The opposite of filter method
+   * Iterable of all elements `f` returns falsy for
+   *
+   * see {@link https://fxts.dev/docs/reject | reject}
+   */
+  reject(f: (a: A) => unknown): FxIterable<A> {
+    return new FxIterable(reject(f, this.iterable));
+  }
+
+  /**
+   * Returns Iterable that taken the first argument `l` values from iterable
+   *
+   * see {@link https://fxts.dev/docs/take | take}
+   */
+  take(n: number): FxIterable<A> {
+    return new FxIterable(take(n, this.iterable));
+  }
+
+  /**
+   * Returns Iterable that taken values until truthy when given `f` is applied.
+   *
+   * see {@link https://fxts.dev/docs/takeUntil | takeUntil}
+   */
+  takeUntil(f: (a: A) => unknown): FxIterable<A> {
+    return new FxIterable(takeUntil(f, this.iterable));
+  }
+
+  /**
+   * Returns Iterable that taken values as long as each value satisfies the give `f`.
+   *
+   * see {@link https://fxts.dev/docs/takeWhile | takeWhile}
+   */
+  takeWhile(f: (a: A) => unknown): FxIterable<A> {
+    return new FxIterable(takeWhile(f, this.iterable));
+  }
+
+  /**
+   * Iterate over an input list,
+   * calling a provided `f` for each element in the Iterable.
+   *
+   * see {@link https://fxts.dev/docs/peek | peek}
+   */
+  peek(f: (a: A) => unknown): FxIterable<A> {
+    return new FxIterable(peek(f, this.iterable));
+  }
+
+  /**
+   * Returns all but the first `length` elements of the given iterable.
+   *
+   * see {@link https://fxts.dev/docs/drop | drop}
+   */
+  drop(length: number): FxIterable<A> {
+    return new FxIterable(drop(length, this.iterable));
+  }
+
+  /**
+   * Returns Iterable of the given elements from startIndex(inclusive) to endIndex(exclusive).
+   *
+   * see {@link https://fxts.dev/docs/slice | slice}
+   */
+  slice(start: number, end?: number): FxIterable<A> {
+    return isUndefined(end)
+      ? new FxIterable(slice(start, this.iterable))
+      : new FxIterable(slice(start, end, this.iterable));
+  }
+
+  /**
+   *
+   * `chain` allows you to use functions that are not provided in method chaining.
+   * The functions available for the `chain` argument return an asyncIterable.
+   *
+   * @example
+   * ```
+   * fx(range(1, 4))
+   *   .chain(append(4))
+   *   .chain(append(5))
+   *   .toArray(); // [1, 2, 3, 4, 5]
+   * ```
+   */
+  chain<B>(f: (iterable: Iterable<A>) => Iterable<B>): FxIterable<B> {
+    return new FxIterable(f(this.iterable));
+  }
+
+  /**
+   * Returns AsyncIterable, `toAsync` used when you want to handle Promise values inside Iterable.
+   *
+   * see {@link https://fxts.dev/docs/toAsync | toAsync}
+   */
+  toAsync(): FxAsyncIterable<A> {
+    return new FxAsyncIterable(toAsync(this.iterable));
+  }
+
+  /**
+   * Splits Iterable into sets, grouped by the result of running each value through `f`.
+   *
+   * see {@link https://fxts.dev/docs/groupBy | groupBy}
+   */
+  groupBy(f: (a: A) => Key) {
+    return groupBy(f, this.iterable);
+  }
+
+  /**
+   * Given `f` that generates a key,
+   * turns a list of objects into an object indexing the objects by the given key.
+   * Note that if multiple objects generate the same value for the indexing key only the last value will be included in the generated object.
+   *
+   * see {@link https://fxts.dev/docs/indexBy | indexBy}
+   */
+  indexBy(f: (a: A) => Key) {
+    return indexBy(f, this.iterable);
+  }
+
+  /**
+   * Returns true if any of the values in AsyncIterable pass `f` truth test
+   *
+   * see {@link https://fxts.dev/docs/some | some}
+   */
+  some(f: (a: A) => unknown): boolean {
+    return some(f, this.iterable);
+  }
+
+  /**
+   * Returns true if all of the values in AsyncIterable pass the `f` truth test.
+   *
+   * see {@link https://fxts.dev/docs/every | every}
+   */
+  every(f: (a: A) => unknown): boolean {
+    return every(f, this.iterable);
+  }
+
+  /**
+   * Returns all elements in the given iterable into a string separated by separator.
+   *
+   * see {@link https://fxts.dev/docs/join | join}
+   */
+  join(sep: string): string {
+    return join(sep, this.iterable);
+  }
+
+  /**
+   * Looks through each value in AsyncIterable, returning the first one that passes a truth test `f`,
+   * or `undefined` if no value passes the test.
+   *
+   * see {@link https://fxts.dev/docs/find | find}
+   */
+  find(f: (a: A) => unknown): A | undefined {
+    return find(f, this.iterable);
+  }
+
+  /**
+   * Returns the index of the first element of AsyncIterable which matches f, or -1 if no element matches.
+   *
+   * see {@link https://fxts.dev/docs/findIndex | findIndex}
+   */
+  findIndex(f: (a: A) => unknown): number {
+    return findIndex(f, this.iterable);
+  }
+
+  /**
+   * Also known as foldl, this method boils down a list of values into a single value.
+   *
+   * see {@link https://fxts.dev/docs/reduce | reduce}
+   */
+  reduce<B>(f: SyncReducer<Cast<A, B>, A>, seed?: B): Cast<A, B> {
+    return isUndefined(seed)
+      ? reduce(f, this.iterable)
+      : reduce(f, seed as any, this.iterable);
+  }
+
+  /**
+   * Iterates over Iterable, applying each in turn to `f`.
+   *
+   * see {@link https://fxts.dev/docs/each | each}
+   */
+  each(f: (a: A) => unknown): void {
+    return each(f, this.iterable);
+  }
+
+  /**
+   * Takes item from Iterable and returns an array.
+   *
+   * see {@link https://fxts.dev/docs/toArray | toArray}
+   */
+  toArray(): Array<A> {
+    return Array.from(this.iterable);
+  }
+
+  toIterator(): Array<A> {
+    return Array.from(this.iterable);
+  }
+}
+
+/**
+ * `fx` allows functions provided by existing `fxts` to be used in a method chaining.
+ *  Not all functions are provided as methods and can be connected through `chain` if necessary.
+ *
+ *  see {@link https://fxts.dev/docs/method-chaining | guide}
+ *
+ * @example
+ * ```ts
+ * const syncArr1 = fx([1, 2, 3, 4])
+ *   .map((a) => a + 10)
+ *   .toArray(); // [11, 12, 13, 14]
+ *
+ * // If you want to use another function that is not provided for the method, use `chain`.
+ * const syncArr2 = fx([1, 2, 3, 4])
+ *   .chain(append(5))
+ *   .map((a) => a + 10)
+ *   .toArray(); // [11, 12, 13, 14, 15]
+ *
+ * const asyncArr1 = await fx([1, 2, 3, 4])
+ *   .toAsync()
+ *   .map((a) => a + 10)
+ *   .toArray(); // [11, 12, 13, 14]
+ *
+ * const asyncArr2 = await fx(toAsync([1, 2, 3, 4]));
+ *   .map((a) => a + 10)
+ *   .toArray(); // [11, 12, 13, 14]
+ * ```
+ */
+function fx<T extends Iterable<unknown> | AsyncIterable<unknown>>(
+  a: T,
+): T extends Iterable<unknown>
+  ? FxIterable<IterableInfer<T>>
+  : FxAsyncIterable<IterableInfer<T>> {
+  if (isAsyncIterable(a)) {
+    return new FxAsyncIterable(a) as any;
+  } else if (isIterable(a)) {
+    return new FxIterable(a) as any;
+  }
+
+  throw new TypeError(`'fx' must be type of Iterable or AsyncIterable`);
+}
+
+export default fx;
diff --git a/src/Lazy/index.ts b/src/Lazy/index.ts
index 222cb7f0..2f67d87d 100644
--- a/src/Lazy/index.ts
+++ b/src/Lazy/index.ts
@@ -15,6 +15,7 @@ import entries from "./entries";
 import filter from "./filter";
 import flat from "./flat";
 import flatMap from "./flatMap";
+import fx from "./fx";
 import intersection from "./intersection";
 import intersectionBy from "./intersectionBy";
 import keys from "./keys";
@@ -60,6 +61,7 @@ export {
   filter,
   flat,
   flatMap,
+  fx,
   intersection,
   intersectionBy,
   keys,
diff --git a/website/docs_md/method-chaining.md b/website/docs_md/method-chaining.md
new file mode 100644
index 00000000..d65ac208
--- /dev/null
+++ b/website/docs_md/method-chaining.md
@@ -0,0 +1,81 @@
+---
+id: method-chaining
+---
+
+# Method Chaining
+
+You can handle Iterable/AsyncIterable through a [pipe](https://fxts.dev/docs/pipe), but `fxts` also provides data change in the form of method chaining.
+
+```ts
+fx([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
+  .filter((a) => a % 2 === 0) // [0, 2]
+  .map((a) => a * a) // [0, 4]
+  .take(2) // [0, 4]
+  .reduce(sum); // 4
+
+fx("abc")
+  .map((a) => a.toUpperCase()) // ["a", "b"]
+  .take(2)
+  .toArray(); // ["a", "b"]
+```
+
+### Note
+
+Since `fx` defaults to lazy evaluation, it is not actually evaluated until strict evaluation methods such as `toArray`, `groupBy`, `indexBy`, and `some` are executed.
+
+For details on lazy evaluation, please refer to https://fxts.dev/docs/lazy-evaluation.
+
+### Support for handling AsyncIterable
+
+`fx` can also handle [AsyncIterator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncIterator) values. `toAsync` is used in the example below to create an `AsyncIterator` value.
+
+```ts
+await fx(toAsync([1, 2, 3, 4]))
+  .filter(async (a) => a % 2 === 0)
+  .map(async (a) => a * a)
+  .reduce(sum);
+
+await fx([1, 2, 3, 4])
+  .filter((a) => a % 2 === 0)
+  .toAsync() // if async function returns
+  .map(async (a) => a * a)
+  .reduce(sum);
+```
+
+### Handle Concurrency
+
+`fx` supports concurrent operation. As we saw in concurrent, concurrent can only be used in asyncIterable.
+
+For details on handling concurrent with `fxts`, please refer to https://fxts.dev/docs/handle-concurrency
+
+```ts
+/**
+ *
+ *  evaluation
+ *               ┌─────┐  ┌─────┐  ┌─────┐  ┌─────┐  ┌─────┐  ┌─────┐
+ *               │  1  │──│  2  │──│  3  │──│  4  │──│  5  │──│  6  │
+ *               └──┬──┘  └──┬──┘  └──┬──┘  └──┬──┘  └──┬──┘  └──┬──┘
+ *       map        │        │        │        │        │        │
+ *  concurrent(2)  (1)      (1)      (2)      (2)      (3)      (3)
+ *                  │        │        │        │        │        │
+ *                  ▼        ▼        ▼        ▼        ▼        ▼
+ */
+await fx(toAsync(range(1, 7)))
+  // async function returns
+  .map(async (a) => delay(100, a))
+  .concurrent(2)
+  .consume(); // It takes approximately 300ms.
+```
+
+### Etc
+
+`fx` does not provide all the functions of `fxts` as methods.
+
+If you want to use the `fxts` function which is not provided or additional functions, you can use the `chain` method.
+
+```ts
+fx([1, 2, 3, 4])
+  .chain(append(5))
+  .map((a) => a + 10)
+  .toArray(); // [11, 12, 13, 14, 15]
+```
diff --git a/website/function.json b/website/function.json
index d6d33600..902836aa 100644
--- a/website/function.json
+++ b/website/function.json
@@ -17,6 +17,7 @@
     "filter",
     "flat",
     "flatMap",
+    "fx",
     "intersection",
     "intersectionBy",
     "keys",
diff --git a/website/sidebars.js b/website/sidebars.js
index 257f7044..39615f04 100644
--- a/website/sidebars.js
+++ b/website/sidebars.js
@@ -71,6 +71,11 @@ const sidebars = {
           id: "error-handling",
           label: "Error handling",
         },
+        {
+          type: "doc",
+          id: "method-chaining",
+          label: "Method Chaining",
+        },
       ],
     },
     {
diff --git a/website/tsdoc-metadata.json b/website/tsdoc-metadata.json
index 5e804b3b..22735db1 100644
--- a/website/tsdoc-metadata.json
+++ b/website/tsdoc-metadata.json
@@ -5,7 +5,7 @@
   "toolPackages": [
     {
       "packageName": "@microsoft/api-extractor",
-      "packageVersion": "7.18.19"
+      "packageVersion": "7.42.3"
     }
   ]
 }


Test patch:
diff --git a/test/Lazy/append.spec.ts b/test/Lazy/append.spec.ts
index ee94019a..88caee10 100644
--- a/test/Lazy/append.spec.ts
+++ b/test/Lazy/append.spec.ts
@@ -2,6 +2,7 @@ import {
   append,
   concurrent,
   delay,
+  fx,
   map,
   pipe,
   range,
@@ -25,6 +26,11 @@ describe("append", function () {
       const res = pipe(range(1, 4), append(4), append(5), append(6), toArray);
       expect(res).toEqual([1, 2, 3, 4, 5, 6]);
     });
+
+    it("should be able to be used chaining method with chain in the `fx`", function () {
+      const res = fx(range(1, 4)).chain(append(4)).chain(append(5)).toArray();
+      expect(res).toEqual([1, 2, 3, 4, 5]);
+    });
   });
 
   describe("async", function () {
@@ -42,6 +48,15 @@ describe("append", function () {
       expect(res).toEqual([1, 2, 3, 4]);
     });
 
+    it("should be able to be used chaining method with chain in the `fx`", async function () {
+      const res = await fx(range(1, 4))
+        .toAsync()
+        .chain(append(4))
+        .chain(append(5))
+        .toArray();
+      expect(res).toEqual([1, 2, 3, 4, 5]);
+    });
+
     it("should be appended sequentially", async function () {
       let chainedPromise: Promise<void | number> = Promise.resolve();
       const res = await pipe(
diff --git a/test/Lazy/concurrent.spec.ts b/test/Lazy/concurrent.spec.ts
index 95cf033c..65ad9887 100644
--- a/test/Lazy/concurrent.spec.ts
+++ b/test/Lazy/concurrent.spec.ts
@@ -2,6 +2,7 @@ import {
   concurrent,
   delay,
   filter,
+  fx,
   map,
   peek,
   pipe,
@@ -52,6 +53,14 @@ describe("concurrent", function () {
     expect(arr).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
   }, 550);
 
+  it("should be able to be used as a chaining method in the `fx`", async function () {
+    const arr = await fx(toAsync(range(1, 11)))
+      .map((a) => delay(100, a))
+      .concurrent(2)
+      .toArray();
+    expect(arr).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
+  }, 550);
+
   it("should be affected only one concurrent below it, when nested concurrent", async function () {
     let concurrent10Count = 0;
     let concurrent2Count = 0;
diff --git a/test/Lazy/drop.spec.ts b/test/Lazy/drop.spec.ts
index 0b8265f4..a11adb75 100644
--- a/test/Lazy/drop.spec.ts
+++ b/test/Lazy/drop.spec.ts
@@ -3,6 +3,7 @@ import {
   delay,
   drop,
   filter,
+  fx,
   map,
   pipe,
   toArray,
@@ -33,6 +34,16 @@ describe("drop", function () {
 
       expect(res).toEqual([16, 18]);
     });
+
+    it("should be able to be used as a chaining method in the `fx`", function () {
+      const res = fx([1, 2, 3, 4, 5, 6, 7, 8])
+        .map((a) => a + 10)
+        .filter((a) => a % 2 === 0)
+        .drop(2)
+        .toArray();
+
+      expect(res).toEqual([16, 18]);
+    });
   });
 
   describe("async", function () {
@@ -60,6 +71,16 @@ describe("drop", function () {
       expect(res).toEqual([16, 18]);
     });
 
+    it("should be able to be used as a chaining method in the `fx`", async function () {
+      const res = await fx(toAsync([1, 2, 3, 4, 5, 6, 7, 8]))
+        .map((a) => a + 10)
+        .filter((a) => a % 2 === 0)
+        .drop(2)
+        .toArray();
+
+      expect(res).toEqual([16, 18]);
+    });
+
     it("should be discarded elements by length concurrently", async function () {
       const res = await pipe(
         toAsync([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
diff --git a/test/Lazy/filter.spec.ts b/test/Lazy/filter.spec.ts
index 64800f23..9ec9d251 100644
--- a/test/Lazy/filter.spec.ts
+++ b/test/Lazy/filter.spec.ts
@@ -3,6 +3,7 @@ import {
   concurrent,
   delay,
   filter,
+  fx,
   map,
   pipe,
   range,
@@ -54,6 +55,14 @@ describe("filter", function () {
 
       expect(res).toEqual([2, 4]);
     });
+
+    it("should be able to be used as a chaining method in the `fx`", function () {
+      const res = fx([1, 2, 3, 4])
+        .filter((a) => a % 2 === 0)
+        .toArray();
+
+      expect(res).toEqual([2, 4]);
+    });
   });
 
   describe("async", function () {
@@ -263,6 +272,14 @@ describe("filter", function () {
       expect(res).toEqual([2, 4]);
     });
 
+    it("should be able to be used as a chaining method in the `fx`", async function () {
+      const res = await fx(toAsync([1, 2, 3, 4]))
+        .filter((a) => a % 2 === 0)
+        .toArray();
+
+      expect(res).toEqual([2, 4]);
+    });
+
     it("should be consumed 'AsyncIterable' as many times as called with 'next'", async function () {
       const iterator = toAsync(range(1, 21));
       const res = pipe(
diff --git a/test/Lazy/flat.spec.ts b/test/Lazy/flat.spec.ts
index cb685004..9b06c7fe 100644
--- a/test/Lazy/flat.spec.ts
+++ b/test/Lazy/flat.spec.ts
@@ -4,6 +4,7 @@ import {
   delay,
   filter,
   flat,
+  fx,
   map,
   pipe,
   range,
@@ -37,6 +38,15 @@ describe("flat", function () {
 
       expect(res).toEqual([11, 12, 13, 14, 15]);
     });
+
+    it("should be able to be used as a chaining method in the `fx`", function () {
+      const res = fx([1, 2, 3, [4, 5]])
+        .flat()
+        .map((a) => a + 10)
+        .toArray();
+
+      expect(res).toEqual([11, 12, 13, 14, 15]);
+    });
   });
 
   describe("async", function () {
@@ -64,6 +74,15 @@ describe("flat", function () {
       expect(res).toEqual([11, 12, 13, 14, 15]);
     });
 
+    it("should be able to be used as a chaining method in the `fx`", async function () {
+      const res = await fx(toAsync([1, 2, 3, [4, 5]]))
+        .flat()
+        .map((a) => a + 10)
+        .toArray();
+
+      expect(res).toEqual([11, 12, 13, 14, 15]);
+    });
+
     it("should be flattened concurrently", async function () {
       const iterator = toAsync([
         [1],
diff --git a/test/Lazy/flatMap.spec.ts b/test/Lazy/flatMap.spec.ts
index 59cabcbe..14b31008 100644
--- a/test/Lazy/flatMap.spec.ts
+++ b/test/Lazy/flatMap.spec.ts
@@ -1,4 +1,4 @@
-import { flatMap, map, pipe, toArray, toAsync } from "../../src/index";
+import { flatMap, fx, map, pipe, toArray, toAsync } from "../../src/index";
 import { Concurrent } from "../../src/Lazy/concurrent";
 import { generatorMock } from "../utils";
 
@@ -25,6 +25,15 @@ describe("flatMap", function () {
 
       expect(res).toEqual(["IT", "IS", "A", "GOOD", "DAY"]);
     });
+
+    it("should be able to be used as a chaining method in the `fx`", function () {
+      const res = fx(["It is", "a good", "day"])
+        .flatMap((s) => s.split(" "))
+        .map((a) => a.toUpperCase())
+        .toArray();
+
+      expect(res).toEqual(["IT", "IS", "A", "GOOD", "DAY"]);
+    });
   });
 
   describe("async", function () {
@@ -48,6 +57,15 @@ describe("flatMap", function () {
       expect(res).toEqual(["IT", "IS", "A", "GOOD", "DAY"]);
     });
 
+    it("should be able to be used as a chaining method in the `fx`", async function () {
+      const res = await fx(toAsync(["It is", "a good", "day"]))
+        .flatMap((s) => s.split(" "))
+        .map((a) => a.toUpperCase())
+        .toArray();
+
+      expect(res).toEqual(["IT", "IS", "A", "GOOD", "DAY"]);
+    });
+
     it("should be passed concurrent object when job works concurrently", async function () {
       const mock = generatorMock();
       const iter = flatMap((a) => a, mock);
diff --git a/test/Lazy/fx.spec.ts b/test/Lazy/fx.spec.ts
new file mode 100644
index 00000000..bc6174f1
--- /dev/null
+++ b/test/Lazy/fx.spec.ts
@@ -0,0 +1,27 @@
+import { fx, toAsync } from "../../src";
+
+describe("fx", function () {
+  describe("sync", function () {
+    it("handle fx iterable", function () {
+      const res = fx([1, 2, 3, 4, 5])
+        .map((a) => a + 10)
+        .toArray();
+      expect(res).toEqual([11, 12, 13, 14, 15]);
+    });
+  });
+
+  describe("async", () => {
+    it("handle fx asyncIterable", async function () {
+      const res1 = await fx(toAsync([1, 2, 3, 4, 5]))
+        .map(async (a) => a + 10)
+        .toArray();
+      expect(res1).toEqual([11, 12, 13, 14, 15]);
+
+      const res2 = await fx([1, 2, 3, 4, 5])
+        .toAsync()
+        .map(async (a) => a + 10)
+        .toArray();
+      expect(res2).toEqual([11, 12, 13, 14, 15]);
+    });
+  });
+});
diff --git a/test/Lazy/map.spec.ts b/test/Lazy/map.spec.ts
index 7ce468a6..f2ae9f19 100644
--- a/test/Lazy/map.spec.ts
+++ b/test/Lazy/map.spec.ts
@@ -1,5 +1,5 @@
 import { AsyncFunctionException } from "../../src/_internal/error";
-import { map, pipe, range, toArray, toAsync } from "../../src/index";
+import { fx, map, pipe, range, toArray, toAsync } from "../../src/index";
 import { Concurrent } from "../../src/Lazy/concurrent";
 import { generatorMock } from "../utils";
 
@@ -32,6 +32,16 @@ describe("map", function () {
 
       expect(res).toEqual(["1", "2", "3", "4"]);
     });
+
+    it("should be able to be used as a chaining method in the `fx`", function () {
+      const res = fx([1, 2, 3, 4])
+        .map((a) => a)
+        .map((a) => String(a))
+        .map((a) => a)
+        .toArray();
+
+      expect(res).toEqual(["1", "2", "3", "4"]);
+    });
   });
 
   describe("async", function () {
@@ -83,6 +93,15 @@ describe("map", function () {
       expect(res).toEqual(["1", "2", "3", "4"]);
     });
 
+    it("should be able to be used as a chaining method in the `fx`", async function () {
+      const res = await fx(toAsync([1, 2, 3, 4]))
+        .map((a) => Promise.resolve(a))
+        .map((a) => String(a))
+        .toArray();
+
+      expect(res).toEqual(["1", "2", "3", "4"]);
+    });
+
     it("should be passed concurrent object when job works concurrently", async function () {
       const mock = generatorMock();
       const iter = map((a) => a, mock);
diff --git a/test/Lazy/peek.spec.ts b/test/Lazy/peek.spec.ts
index 88acab4b..b86b33fc 100644
--- a/test/Lazy/peek.spec.ts
+++ b/test/Lazy/peek.spec.ts
@@ -1,4 +1,4 @@
-import { map, peek, pipe, toArray, toAsync } from "../../src/index";
+import { fx, map, peek, pipe, toArray, toAsync } from "../../src/index";
 import { Concurrent } from "../../src/Lazy/concurrent";
 import { generatorMock } from "../utils";
 
@@ -27,6 +27,18 @@ describe("peek", function () {
       expect(res).toEqual([21, 22, 23, 24]);
     });
 
+    it("should be able to be used as a chaining method in the `fx`", function () {
+      let sum = 0;
+      const res = fx([1, 2, 3, 4])
+        .map((a) => a + 10)
+        .peek((a) => (sum = sum + a))
+        .map((a) => a + 10)
+        .toArray();
+
+      expect(sum).toEqual(50);
+      expect(res).toEqual([21, 22, 23, 24]);
+    });
+
     it("should be able to handle an error", function () {
       const res: number[] = [];
       expect(() =>
@@ -70,6 +82,18 @@ describe("peek", function () {
       expect(res).toEqual([21, 22, 23, 24]);
     });
 
+    it("should be able to be used as a chaining method in the `fx`", async function () {
+      let sum = 0;
+      const res = await fx(toAsync([1, 2, 3, 4]))
+        .map((a) => a + 10)
+        .peek((a) => (sum = sum + a))
+        .map((a) => a + 10)
+        .toArray();
+
+      expect(sum).toEqual(50);
+      expect(res).toEqual([21, 22, 23, 24]);
+    });
+
     it("should be able to handle an error", async function () {
       const res: number[] = [];
 
diff --git a/test/Lazy/slice.spec.ts b/test/Lazy/slice.spec.ts
index a6023bae..cb60e583 100644
--- a/test/Lazy/slice.spec.ts
+++ b/test/Lazy/slice.spec.ts
@@ -1,4 +1,4 @@
-import { pipe, slice, toArray, toAsync } from "../../src";
+import { fx, pipe, slice, toArray, toAsync } from "../../src";
 import { Concurrent } from "../../src/Lazy/concurrent";
 import { generatorMock } from "../utils";
 
@@ -41,6 +41,14 @@ describe("slice", function () {
       const res2 = pipe([1, 2, 3, 4, 5], slice(1, 3), toArray);
       expect(res2).toEqual([2, 3]);
     });
+
+    it("should be able to be used as a chaining method in the `fx`", function () {
+      const res1 = fx([1, 2, 3, 4, 5]).slice(2).toArray();
+      expect(res1).toEqual([3, 4, 5]);
+
+      const res2 = fx([1, 2, 3, 4, 5]).slice(1, 3).toArray();
+      expect(res2).toEqual([2, 3]);
+    });
   });
 
   describe("async", function () {
@@ -84,6 +92,14 @@ describe("slice", function () {
       expect(res2).toEqual([2, 3]);
     });
 
+    it("should be able to be used as a chaining method in the `fx`", async function () {
+      const res1 = await fx([1, 2, 3, 4, 5]).toAsync().slice(2).toArray();
+      expect(res1).toEqual([3, 4, 5]);
+
+      const res2 = await fx([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);
diff --git a/test/Lazy/take.spec.ts b/test/Lazy/take.spec.ts
index 7c96fa82..cd23419c 100644
--- a/test/Lazy/take.spec.ts
+++ b/test/Lazy/take.spec.ts
@@ -1,6 +1,7 @@
 import {
   delay,
   filter,
+  fx,
   map,
   pipe,
   range,
@@ -51,6 +52,16 @@ describe("take", function () {
       expect(res1).toEqual([12, 14]);
     });
 
+    it("should be able to be used as a chaining method in the `fx`", function () {
+      const res1 = fx([1, 2, 3, 4])
+        .map((a) => a + 10)
+        .filter((a) => a % 2 === 0)
+        .take(2)
+        .toArray();
+
+      expect(res1).toEqual([12, 14]);
+    });
+
     it("should be able to take the rest element", async function () {
       const iter = take(5, range(1, 11));
       iter.next();
@@ -101,6 +112,16 @@ describe("take", function () {
 
       expect(res1).toEqual([12, 14]);
     });
+
+    it("should be able to be used as a chaining method in the `fx`", async function () {
+      const res1 = await fx(toAsync([1, 2, 3, 4]))
+        .map((a) => a + 10)
+        .filter((a) => a % 2 === 0)
+        .take(2)
+        .toArray();
+
+      expect(res1).toEqual([12, 14]);
+    });
     it("should be able to take the element concurrently", async function () {
       const asyncIterator = take(
         3,
diff --git a/test/Lazy/takeUntil.spec.ts b/test/Lazy/takeUntil.spec.ts
index 8deeddd0..a1720b2c 100644
--- a/test/Lazy/takeUntil.spec.ts
+++ b/test/Lazy/takeUntil.spec.ts
@@ -3,6 +3,7 @@ import {
   concurrent,
   delay,
   filter,
+  fx,
   map,
   peek,
   pipe,
@@ -46,6 +47,16 @@ describe("takeUntil", function () {
 
       expect(res).toEqual([12, 14]);
     });
+
+    it("should be able to be used as a chaining method in the `fx`", function () {
+      const res = fx([1, 2, 3, 4])
+        .map((a) => a + 10)
+        .filter((a) => a % 2 === 0)
+        .takeUntil((a) => a > 12)
+        .toArray();
+
+      expect(res).toEqual([12, 14]);
+    });
   });
 
   describe("async", function () {
@@ -96,6 +107,16 @@ describe("takeUntil", function () {
       expect(res).toEqual([12, 14]);
     });
 
+    it("should be able to be used as a chaining method in the `fx`", async function () {
+      const res = await fx(toAsync([1, 2, 3, 4]))
+        .map((a) => a + 10)
+        .filter((a) => a % 2 === 0)
+        .takeUntil((a) => a > 12)
+        .toArray();
+
+      expect(res).toEqual([12, 14]);
+    });
+
     it("should be able to take the element", async function () {
       const fn = jest.fn();
       const res = await pipe(
diff --git a/test/Lazy/takeWhile.spec.ts b/test/Lazy/takeWhile.spec.ts
index da3c5138..6cde1e70 100644
--- a/test/Lazy/takeWhile.spec.ts
+++ b/test/Lazy/takeWhile.spec.ts
@@ -3,6 +3,7 @@ import {
   concurrent,
   delay,
   filter,
+  fx,
   map,
   pipe,
   range,
@@ -45,6 +46,16 @@ describe("takeWhile", function () {
 
       expect(res).toEqual([12, 14, 16, 18]);
     });
+
+    it("should be able to be used as a chaining method in the `fx`", function () {
+      const res = fx(range(1, 20))
+        .map((a) => a + 10)
+        .filter((a) => a % 2 === 0)
+        .takeWhile((a) => a < 20)
+        .toArray();
+
+      expect(res).toEqual([12, 14, 16, 18]);
+    });
   });
 
   describe("async", function () {
@@ -95,6 +106,17 @@ describe("takeWhile", function () {
       expect(res).toEqual([12, 14, 16, 18]);
     });
 
+    it("should be able to be used as a chaining method in the `fx`", async function () {
+      const res = await fx(range(1, 20))
+        .toAsync()
+        .map((a) => a + 10)
+        .filter((a) => a % 2 === 0)
+        .takeWhile((a) => a < 20)
+        .toArray();
+
+      expect(res).toEqual([12, 14, 16, 18]);
+    });
+
     it("should be consumed 'AsyncIterable' as many times as called with 'next'", async function () {
       const res = pipe(
         toAsync(range(1, 500)),
diff --git a/test/each.spec.ts b/test/each.spec.ts
index 37e75e1a..57af41d8 100644
--- a/test/each.spec.ts
+++ b/test/each.spec.ts
@@ -1,4 +1,4 @@
-import { each, map, pipe, range, toAsync } from "../src";
+import { each, fx, map, pipe, range, toAsync } from "../src";
 
 describe("each", function () {
   describe("sync", function () {
@@ -24,6 +24,16 @@ describe("each", function () {
       );
       expect(acc).toEqual(50);
     });
+
+    it("should be able to be used as a chaining method in the `fx`", function () {
+      let acc = 0;
+      fx([1, 2, 3, 4])
+        .map((a) => a + 10)
+        .each((a) => {
+          acc += a;
+        });
+      expect(acc).toEqual(50);
+    });
   });
 
   describe("async", function () {
@@ -101,5 +111,15 @@ describe("each", function () {
       );
       expect(res2).toEqual(50);
     });
+
+    it("should be able to be used as a chaining method in the `fx`", async function () {
+      let acc = 0;
+      await fx(toAsync([1, 2, 3, 4]))
+        .map((a) => a + 10)
+        .each((a) => {
+          acc += a;
+        });
+      expect(acc).toEqual(50);
+    });
   });
 });
diff --git a/test/every.spec.ts b/test/every.spec.ts
index a8b1d590..fdbeb929 100644
--- a/test/every.spec.ts
+++ b/test/every.spec.ts
@@ -1,4 +1,4 @@
-import { every, filter, map, pipe, toAsync } from "../src";
+import { every, filter, fx, map, pipe, toAsync } from "../src";
 import { AsyncFunctionException } from "../src/_internal/error";
 
 describe("every", function () {
@@ -39,6 +39,23 @@ describe("every", function () {
       expect(res3).toEqual(false);
     });
 
+    it("should be able to be used as a chaining method in the `fx`", function () {
+      const res1 = fx([1, 2, 3, 4, 5, 6, 7, 8, 9])
+        .filter((a) => a % 2 === 0)
+        .every((a) => a % 2 === 0);
+      expect(res1).toEqual(true);
+
+      const res2 = fx([1, 2, 3, 4, 5, 6, 7, 8, 9])
+        .map((a) => a + 10)
+        .every((a) => a > 10);
+      expect(res2).toEqual(true);
+
+      const res3 = fx([1, 2, 3, 4, 5, 6, 7, 8, 9])
+        .map((a) => a + 10)
+        .every((a) => a < 10);
+      expect(res3).toEqual(false);
+    });
+
     it("should throw an error when the callback is asynchronous", function () {
       expect(() =>
         pipe(
@@ -86,4 +103,21 @@ describe("every", function () {
     );
     expect(res3).toEqual(false);
   });
+
+  it("should be able to be used as a chaining method in the `fx`", async function () {
+    const res1 = await fx(toAsync([1, 2, 3, 4, 5, 6, 7, 8, 9]))
+      .filter((a) => a % 2 === 0)
+      .every((a) => a % 2 === 0);
+    expect(res1).toEqual(true);
+
+    const res2 = await fx(toAsync([1, 2, 3, 4, 5, 6, 7, 8, 9]))
+      .map((a) => a + 10)
+      .every((a) => a > 10);
+    expect(res2).toEqual(true);
+
+    const res3 = await fx(toAsync([1, 2, 3, 4, 5, 6, 7, 8, 9]))
+      .map((a) => a + 10)
+      .every((a) => a < 10);
+    expect(res3).toEqual(false);
+  });
 });
diff --git a/test/find.spec.ts b/test/find.spec.ts
index f70ff03c..bfb65d4d 100644
--- a/test/find.spec.ts
+++ b/test/find.spec.ts
@@ -1,4 +1,4 @@
-import { filter, find, map, pipe, toAsync } from "../src";
+import { filter, find, fx, map, pipe, toAsync } from "../src";
 import { AsyncFunctionException } from "../src/_internal/error";
 import type Arrow from "../src/types/Arrow";
 
@@ -25,6 +25,14 @@ describe("find", function () {
       expect(res1).toEqual(14);
     });
 
+    it("should be able to be used as a chaining method in the `fx`", function () {
+      const res1 = fx([1, 2, 3, 4])
+        .map((a) => a + 10)
+        .filter((a) => a % 2 === 0)
+        .find((a) => a === 14);
+      expect(res1).toEqual(14);
+    });
+
     it("should throw an error when the callback is asynchronous", function () {
       const res = () =>
         pipe(
@@ -56,4 +64,12 @@ describe("find", function () {
     );
     expect(res1).toEqual(14);
   });
+
+  it("should be able to be used as a chaining method in the `fx`", async function () {
+    const res1 = await fx(toAsync([1, 2, 3, 4]))
+      .map((a) => a + 10)
+      .filter((a) => a % 2 === 0)
+      .find((a) => a === 14);
+    expect(res1).toEqual(14);
+  });
 });
diff --git a/test/findIndex.spec.ts b/test/findIndex.spec.ts
index e11a0e98..26c6f77b 100644
--- a/test/findIndex.spec.ts
+++ b/test/findIndex.spec.ts
@@ -1,4 +1,4 @@
-import { filter, findIndex, map, pipe, toAsync } from "../src";
+import { filter, findIndex, fx, map, pipe, toAsync } from "../src";
 import { AsyncFunctionException } from "../src/_internal/error";
 import type Arrow from "../src/types/Arrow";
 
@@ -25,6 +25,14 @@ describe("findIndex", function () {
       expect(res1).toEqual(1);
     });
 
+    it("should be able to be used as a chaining method in the `fx`", function () {
+      const res1 = fx([1, 2, 3, 4])
+        .map((a) => a + 10)
+        .filter((a) => a % 2 === 0)
+        .findIndex((a) => a === 14);
+      expect(res1).toEqual(1);
+    });
+
     it("should throw an error when the callback is asynchronous", function () {
       const res = () =>
         pipe(
@@ -56,4 +64,12 @@ describe("findIndex", function () {
     );
     expect(res1).toEqual(1);
   });
+
+  it("should be able to be used as a chaining method in the `fx`", async function () {
+    const res1 = await fx(toAsync([1, 2, 3, 4]))
+      .map((a) => a + 10)
+      .filter((a) => a % 2 === 0)
+      .findIndex((a) => a === 14);
+    expect(res1).toEqual(1);
+  });
 });
diff --git a/test/groupBy.spec.ts b/test/groupBy.spec.ts
index 39ba5d53..f42aecd5 100644
--- a/test/groupBy.spec.ts
+++ b/test/groupBy.spec.ts
@@ -1,4 +1,4 @@
-import { filter, groupBy, pipe, toAsync } from "../src";
+import { filter, fx, groupBy, pipe, toAsync } from "../src";
 import { AsyncFunctionException } from "../src/_internal/error";
 
 type Obj = {
@@ -52,6 +52,14 @@ describe("groupBy", function () {
       expect(res).toEqual(then2);
     });
 
+    it("should be able to be used as a chaining method in the `fx`", function () {
+      const res = fx(given)
+        .filter((a) => a.category !== "clothes")
+        .groupBy((a) => a.category);
+
+      expect(res).toEqual(then2);
+    });
+
     it("should throw an error when the callback is asynchronous", function () {
       const res = () =>
         pipe(
@@ -78,5 +86,13 @@ describe("groupBy", function () {
 
       expect(res).toEqual(then2);
     });
+
+    it("should be able to be used as a chaining method in the `fx`", async function () {
+      const res = await fx(toAsync(given))
+        .filter((a) => a.category !== "clothes")
+        .groupBy((a) => a.category);
+
+      expect(res).toEqual(then2);
+    });
   });
 });
diff --git a/test/indexBy.spec.ts b/test/indexBy.spec.ts
index b9cca15c..775b09d9 100644
--- a/test/indexBy.spec.ts
+++ b/test/indexBy.spec.ts
@@ -1,4 +1,4 @@
-import { filter, indexBy, pipe, toAsync } from "../src";
+import { filter, fx, indexBy, pipe, toAsync } from "../src";
 import { AsyncFunctionException } from "../src/_internal/error";
 
 type Obj = {
@@ -36,6 +36,13 @@ describe("indexBy", function () {
       expect(res).toEqual(then2);
     });
 
+    it("should be able to be used as a chaining method in the `fx`", function () {
+      const res = fx(given)
+        .filter((a) => a.category !== "clothes")
+        .indexBy((a) => a.category);
+      expect(res).toEqual(then2);
+    });
+
     it("should throw an error when the callback is asynchronous", function () {
       const res = () =>
         pipe(
@@ -61,5 +68,12 @@ describe("indexBy", function () {
       );
       expect(res).toEqual(then2);
     });
+
+    it("should be able to be used as a chaining method in the `fx`", async function () {
+      const res = await fx(toAsync(given))
+        .filter((a) => a.category !== "clothes")
+        .indexBy((a) => a.category);
+      expect(res).toEqual(then2);
+    });
   });
 });
diff --git a/test/join.spec.ts b/test/join.spec.ts
index de7e86a9..0a58f569 100644
--- a/test/join.spec.ts
+++ b/test/join.spec.ts
@@ -1,4 +1,4 @@
-import { filter, join, map, pipe, toAsync } from "../src";
+import { filter, fx, join, map, pipe, toAsync } from "../src";
 import { asyncEmpty, empty } from "../src/_internal/utils";
 
 describe("join", function () {
@@ -28,6 +28,14 @@ describe("join", function () {
       expect(res).toEqual("12-14-16");
     });
 
+    it("should be able to be used as a chaining method in the `fx`", function () {
+      const res = fx([1, 2, 3, 4, 5, 6, 7])
+        .map((a) => a + 10)
+        .filter((a) => a % 2 === 0)
+        .join("-");
+      expect(res).toEqual("12-14-16");
+    });
+
     it("should return an empty string when it is an empty array", function () {
       expect(join("~", [])).toEqual("");
     });
@@ -54,6 +62,14 @@ describe("join", function () {
       expect(res).toEqual("12-14-16");
     });
 
+    it("should be able to be used as a chaining method in the `fx`", async function () {
+      const res = await fx(toAsync([1, 2, 3, 4, 5, 6, 7]))
+        .map((a) => a + 10)
+        .filter((a) => a % 2 === 0)
+        .join("-");
+      expect(res).toEqual("12-14-16");
+    });
+
     it("should be able to handle an error when asynchronous", async function () {
       await expect(
         pipe(
diff --git a/test/reduce.spec.ts b/test/reduce.spec.ts
index 5c1335ea..28539fd2 100644
--- a/test/reduce.spec.ts
+++ b/test/reduce.spec.ts
@@ -1,4 +1,4 @@
-import { filter, map, pipe, range, reduce, toAsync } from "../src";
+import { filter, fx, map, pipe, range, reduce, toAsync } from "../src";
 
 const addNumber = (a: number, b: number) => a + b;
 const addNumberAsync = async (a: number, b: number) => a + b;
@@ -30,6 +30,14 @@ describe("reduce", function () {
       );
       expect(res).toEqual(1 + 3 + 5);
     });
+
+    it("should be able to be used as a chaining method in the `fx`", function () {
+      const res = fx(["1", "2", "3", "4", "5"])
+        .map((a) => Number(a))
+        .filter((a) => a % 2)
+        .reduce(addNumber);
+      expect(res).toEqual(1 + 3 + 5);
+    });
   });
 
   describe("async", function () {
@@ -109,8 +117,17 @@ describe("reduce", function () {
         filter((a) => a % 2),
         reduce(addNumberAsync),
       );
-      expect(res1).toEqual(1 + 3 + 5);
-      expect(res2).toEqual(1 + 3 + 5);
+      expect(res1).toEqual(9);
+      expect(res2).toEqual(9);
+    });
+
+    it("should be able to be used as a chaining method in the `fx`", async function () {
+      const res1 = await fx(toAsync(["1", "2", "3", "4", "5"]))
+        .map((a) => Number(a))
+        .filter((a) => a % 2)
+        .reduce(addNumber);
+
+      expect(res1).toEqual(9);
     });
   });
 });
diff --git a/test/some.spec.ts b/test/some.spec.ts
index df3f4aff..dcf6b974 100644
--- a/test/some.spec.ts
+++ b/test/some.spec.ts
@@ -1,4 +1,4 @@
-import { filter, map, pipe, some, toAsync } from "../src";
+import { filter, fx, map, pipe, some, toAsync } from "../src";
 import { AsyncFunctionException } from "../src/_internal/error";
 
 describe("some", function () {
@@ -50,6 +50,28 @@ describe("some", function () {
       expect(res4).toEqual(true);
     });
 
+    it("should be able to be used as a chaining method in the `fx`", function () {
+      const res1 = fx([1, 2, 3, 4, 5, 6, 7, 8, 9])
+        .filter((a) => a % 2 === 0)
+        .some((a) => a % 2 === 0);
+      expect(res1).toEqual(true);
+
+      const res2 = fx([1, 2, 3, 4, 5, 6, 7, 8, 9])
+        .map((a) => a + 10)
+        .some((a) => a > 10);
+      expect(res2).toEqual(true);
+
+      const res3 = fx([1, 2, 3, 4, 5, 6, 7, 8, 9])
+        .map((a) => a + 10)
+        .some((a) => a < 10);
+      expect(res3).toEqual(false);
+
+      const res4 = fx([1, 2, 3, 4, 5, 6, 7, 8, 9])
+        .map((a) => a + 10)
+        .some((a) => a < 15);
+      expect(res4).toEqual(true);
+    });
+
     it("should throw an error when the callback is asynchronous", function () {
       expect(() =>
         pipe(
@@ -136,5 +158,27 @@ describe("some", function () {
       );
       expect(res4).toEqual(true);
     });
+
+    it("should be able to be used as a chaining method in the `fx`", async function () {
+      const res1 = await fx(toAsync([1, 2, 3, 4, 5, 6, 7, 8, 9]))
+        .filter((a) => a % 2 === 0)
+        .some((a) => a % 2 === 0);
+      expect(res1).toEqual(true);
+
+      const res2 = await fx(toAsync([1, 2, 3, 4, 5, 6, 7, 8, 9]))
+        .map((a) => a + 10)
+        .some((a) => a > 10);
+      expect(res2).toEqual(true);
+
+      const res3 = await fx(toAsync([1, 2, 3, 4, 5, 6, 7, 8, 9]))
+        .map((a) => a + 10)
+        .some((a) => Promise.resolve(a < 10));
+      expect(res3).toEqual(false);
+
+      const res4 = await fx(toAsync([1, 2, 3, 4, 5, 6, 7, 8, 9]))
+        .map((a) => a + 10)
+        .some((a) => Promise.resolve(a < 15));
+      expect(res4).toEqual(true);
+    });
   });
 });
diff --git a/type-check/Lazy/fx.test.ts b/type-check/Lazy/fx.test.ts
new file mode 100644
index 00000000..ecbf9d1b
--- /dev/null
+++ b/type-check/Lazy/fx.test.ts
@@ -0,0 +1,32 @@
+import { fx, toAsync } from "../../src";
+import type Cast from "../../src/types/Cast";
+import * as Test from "../../src/types/Test";
+
+const { checks, check } = Test;
+
+const res1 = fx([1, 2, 3]);
+const res2 = fx([1, 2, 3])
+  .map((a) => a)
+  .toArray();
+const res3 = fx([1, 2, 3])
+  .map((a) => String(a))
+  .toArray();
+const res4 = fx([1, 2, 3])
+  .map((a) => String(a))
+  .join("");
+
+const res5 = fx([1, 2, 3]).toAsync();
+const res6 = fx(toAsync([1, 2, 3]));
+const res7 = fx(toAsync([1, 2, 3]))
+  .map((a) => a)
+  .toArray();
+
+checks([
+  check<typeof res1, Cast<Iterable<number>, typeof res1>, Test.Pass>(),
+  check<typeof res2, number[], Test.Pass>(),
+  check<typeof res3, string[], Test.Pass>(),
+  check<typeof res4, string, Test.Pass>(),
+  check<typeof res5, Cast<AsyncIterable<number>, typeof res5>, Test.Pass>(),
+  check<typeof res6, Cast<AsyncIterable<number>, typeof res5>, Test.Pass>(),
+  check<typeof res7, Promise<number[]>, Test.Pass>(),
+]);